The beginner's curse of not knowing when to ask "why"
Some of my blog's highlights are: Check out my Github or email me to get in touch
Rust concepts I wish I learned earlier
This past month, I have been enthralled by the Rust programming language given its unique edge for writing memory-safe, modern programs. Over the years, several languages have emerged as the most preferred by engineers to write resilient, backend software. The tides have shifted from Java/C++ into Go and Rust, which combine decades of programming language theory to build tools that are effective in our current age.
Rust’s numbers speak for themselves. As the number 1 most loved language in the famous stack overflow survey for seven years in a row, it has also recently been released as part of Linux kernel - a feat no language other than C has been able to accomplish. What’s exciting about the language, to me, is that it provides something truly new in the art of how software is built.
use std::thread;
use std::time::Duration;
use std::{collections::VecDeque, sync::Condvar, sync::Mutex};
fn main() {
let queue = Mutex::new(VecDeque::new());
thread::scope(|s| {
let t = s.spawn(|| loop {
let item = queue.lock().unwrap().pop_front();
if let Some(item) = item {
dbg!(item);
} else {
thread::park();
}
});
for i in 0.. {
queue.lock().unwrap().push_back(i);
t.thread().unpark();
thread::sleep(Duration::from_secs(1));
}
})
}
How to Set Up an Ethereum Proof-of-Stake Devnet in Minutes
With Ethereum having finally transitioned to proof-of-stake, many people are wondering how to set up a local testing environment given so much has changed.
When Years of Work Finally Materialize
Back in late 2017, I was curious about a particular technology called Ethereum when I understood its incredible potential to change the world. I assembled a team of software engineers I met on the Internet that shared this vision and for 4 years, we have worked tirelessly to upgrade the system to one that is more economically and environmentally sustainable.
Using Interface Composition in Go As Guardrails
Composition over inheritance
Go, as a programming language, favors simplicity. When writing abstractions in Go, interfaces are some of the most powerful tools available to developers, providing a whole suite of useful functionality for your applications and expressive packages.
When a Solution Is Right In Front of You
We had a crazy week debugging one of the trickiest issues we've seen in our software. This post showcases how we went down a rabbit hole of information only to conclude a resolution was far easier than we thought.
Reuse Expensive Computation With In-Progress Caches in Go
Caching is the go-to solution in applications to avoid repeating expensive computation and instead prefer some value that can be readily fetched in-memory. A simple caching strategy is to use a cache as a thin layer above database read access as follows:
package main
import "sync"
type Database struct {
cache map[string][]byte
lock sync.RWMutex
}
func (db *Database) GetItem(key []byte) ([]byte, error) {
db.lock.RLock()
if value, ok := db.cache[string(key)]; ok {
db.lock.RUnlock()
return value
}
db.lock.RUnlock()
return db.readFromDatabase(key)
}
func (db *Database) WriteItem(key, value []byte) error {
if err := db.writeToDatabase(key, value); err != nil {
return err
}
db.lock.Lock()
db.cache[string(key)] = value
db.lock.Unlock()
return nil
}
This strategy works great for applications where you have requests to read access for a certain value repeatedly, preventing you from performing a potentially expensive db query and leveraging fast access in-memory. Caching is very helpful. For some problems, however, a cache is definitely not enough.