Blog
Thoughts on backend engineering, architecture, and web development.
Race Conditions in Node.js: They Exist and They'll Bite You
Node.js is single-threaded, but race conditions are real. Here's how they happen in async code and how to prevent them.
Bun vs Node.js: A Practical Comparison for Backend Engineers
Bun promises speed. But is it ready for production backend work? A real-world comparison across the things that actually matter.
The N+1 Query Problem: Why Your API is Slow
The N+1 problem is the most common performance killer in backend applications. Here's how to detect and fix it across different ORMs.
Designing Idempotent APIs: Why Your Payment Endpoint Charges Twice
Your user clicked 'Pay' twice and got charged twice. Here's how to design APIs that handle retries, network failures, and duplicate requests without duplicating side effects.
The Node.js Event Loop: Beyond the Basics
Most explanations of the event loop are oversimplified. Here's what actually happens, phase by phase, and why it matters.
Docker Multi-Stage Builds: From 1.2GB to 80MB
A real walkthrough of shrinking a Node.js Docker image from 1.2GB to 80MB using multi-stage builds, layer caching, distroless bases, and a production-ready Dockerfile template.
Database Indexing: What Every Backend Engineer Should Know
Indexes can make or break your application performance. Here's how they work internally and when to use them.
Big O Notation: The Practical Guide for Working Engineers
Forget the academic proofs. Here's how to spot O(n^2) hiding in your code, why hash maps change everything, and real refactoring examples that turned minutes into milliseconds.
Database Connection Pooling: Why Your Server Crashes at 100 Concurrent Users
You're opening a new database connection for every request and wondering why your server dies under load. Here's how connection pooling works and how to configure it properly.
API Rate Limiting Done Right: Algorithms and Implementation
Rate limiting protects your API from abuse. Here's how the major algorithms work and which one to pick for your use case.
TypeScript Strict Mode: Stop Using `any` and Start Shipping Safer Code
Every `any` in your codebase is a bug waiting to happen. Here's how to enable strict mode, survive the migration, and use TypeScript the way it was meant to be used.
Git Rebase vs Merge: When to Use Which
A practical guide to rebase vs merge from someone who's destroyed production history and lived to tell about it. Interactive rebase, fixup, squash, and real team workflows.
Git Bisect: Find the Bug in 10 Steps, Not 10 Hours
Binary search through your commit history to find exactly which commit broke things. Manual, automated, and CI-integrated approaches.
Redis Beyond Caching: Queues, Pub/Sub, and Rate Limiters
Redis is not just a cache. I've used it as a message queue, a rate limiter, a leaderboard engine, and a real-time event bus. Here's how, and when to stop.
Graceful Shutdown in Node.js: Stop Killing Your Users' Requests
Your Node.js server gets SIGTERM, immediately dies, and 47 users get 502 errors. Here's how to shut down properly in production.
MongoDB Aggregation Pipeline: Replace Your Application Logic with Database Logic
Stop fetching all documents and filtering in JavaScript. MongoDB's aggregation pipeline can do joins, grouping, pagination, and analytics in a single query.
Hash Tables Under the Hood: Why Object Lookup is O(1)
Hash tables power objects, dictionaries, sets, and caches. Here's what actually happens when you write obj[key], and when O(1) breaks down.
Error Handling Patterns in Node.js: Stop Swallowing Errors
Empty catch blocks, generic error messages, and silent failures. Here are the patterns I use to handle errors properly in production Node.js applications.
No posts found.