Logo
Logo
  • JWT has become the default answer to “how should I handle authentication tokens?” in the Go community. I have shipped JWTs in production and I have also shipped systems that would have been much simpler and more secure with server-side sessions. The point of this lesson is not that JWTs are bad — it is that they carry specific security risks that are easy to overlook, and they solve a specific problem (stateless authentication across services) that not every application actually has.

    Go tutorial golang security Created Sat, 18 Jan 2025 00:00:00 +0000
  • Regular iterators give you one value at a time, synchronously. Futures give you one value, asynchronously. Streams give you multiple values, asynchronously. It’s the obvious combination, and once you start using them, you’ll wonder how you ever processed sequences of async data without them.

    I first needed streams when building a log aggregator. I had dozens of log sources, each producing lines at their own pace. I needed to merge them, filter them, and process them in real time. Without streams, that code was a tangled mess of channels and select loops. With streams, it was a pipeline.

    Rust tutorial rust async tokio Created Thu, 16 Jan 2025 10:31:07 +0000
  • Construction problems break the mold. Most tree problems ask you to read a tree and return something — a traversal, a value, a boolean. Construction problems ask you to create a tree from some compact representation. That reversal in direction exposes a different set of thinking skills: can you reason backwards from output to structure? Can you identify what information each traversal encoding uniquely determines?

    I think of tree construction as a test of how well you understand what information each traversal preserves and destroys. Inorder alone cannot reconstruct a tree. Preorder alone cannot either. But together they contain exactly enough information. Serialize/deserialize is a different angle: design your own encoding so reconstruction is unambiguous. Both problems show up at Google, Meta, and Amazon with surprising regularity.

    fundamentals interviews Created Thu, 16 Jan 2025 00:00:00 +0000
  • We had a service whose p99 latency was 800ms. The p50 was 12ms. The SLA was 200ms at p99. All our dashboards showed the p99 breaching during peak traffic, but the service felt fine to most users. We added caching. We optimized the slow database query. We bumped the CPU allocation. The p99 barely moved.

    Three weeks of tuning later, a colleague asked me to show him the raw histogram buckets. We looked at the actual distribution: 98% of requests were under 15ms. The remaining 2% were all clustered around 900ms, nearly a bimodal distribution. This was not a “slow” service — it was a service with two distinct response time populations, and the p99 was sampling from the slow population. The fix had nothing to do with the code on the hot path.

    Go tutorial golang observability Created Wed, 15 Jan 2025 00:00:00 +0000
  • A couple months ago I was building a WebSocket handler that needed to do three things simultaneously: read from the socket, check a shutdown signal, and send periodic heartbeats. With join!, I’d need all three to complete. But I didn’t want them all to complete — I wanted to react to whichever one happened first.

    That’s what select! does. It races multiple futures and gives you the result of the winner. The losers are dropped.

    Rust tutorial rust async tokio Created Tue, 14 Jan 2025 16:19:55 +0000
  • I remember the exact moment async Rust “clicked” for me. I was building an API aggregator that needed to call five different services. My first version awaited them sequentially — 2 seconds total. Then I spawned them as concurrent tasks — 400ms. Same work, 5x faster, and I didn’t need to think about threads, locks, or shared state.

    But spawning tasks isn’t free, and JoinHandle has some sharp edges that nobody warned me about. This lesson covers the patterns you’ll use every day.

    Rust tutorial rust async tokio Created Sun, 12 Jan 2025 08:44:19 +0000
  • We had a deployment where user preferences stopped being saved. New preferences were accepted by the API — the endpoint returned 200 — but nothing was written to the database. After three hours of debugging we found it: a defer rows.Close() inside a transaction helper that was discarding the error from tx.Commit(). The commit was failing silently, the defer returned without error, and the handler sent a success response. The only indication anything was wrong was a metrics counter nobody had set up an alert for.

    Go tutorial golang code quality Created Sun, 12 Jan 2025 00:00:00 +0000
  • Every async Rust tutorial shows you #[tokio::main] in the first example and then moves on like that’s totally self-explanatory. It’s not. That macro hides a lot of important decisions, and if you don’t understand what it’s doing, you’re going to make some costly mistakes.

    I once spent an entire day debugging a deadlock that happened because I was using the single-threaded runtime without realizing it. One #[tokio::main(flavor = "current_thread")] vs the default, and my entire application’s behavior changed. That shouldn’t surprise anyone — but it surprised me.

    Rust tutorial rust async tokio Created Fri, 10 Jan 2025 11:05:33 +0000
  • Go 1.13 shipped what I consider the most important error-handling improvement the language has had: errors.Is, errors.As, and the %w verb. Before that, wrapping errors with context meant breaking the ability to inspect them. You’d wrap with fmt.Errorf("context: %v", err) and then the original error was gone — you could log it, but you couldn’t match it. Callers would resort to string matching or just give up and let every error map to a 500.

    Go tutorial golang Created Thu, 09 Jan 2025 00:00:00 +0000
  • After I understood the mental model from lesson 1, my next question was obvious: what happens mechanically when I write .await? I could accept “it’s a state machine” as a hand-wave, but I wanted to see the gears turning.

    Turns out, the answer is beautifully simple once you strip away the syntax sugar. The entire async machinery in Rust boils down to one trait, two types, and a contract.

    The Future Trait, For Real This Time

    use std::pin::Pin;
    use std::task::{Context, Poll};
    
    pub trait Future {
        type Output;
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
    }
    

    Three things to note:

    Rust tutorial rust async tokio Created Wed, 08 Jan 2025 14:37:42 +0000