Writing
Notes from 17 years in the codebase.
Practical, opinionated takes on the things I actually build with — no listicles, no hype.
49 articles across 8 topics
All articles
Building Production RAG: Retrieval, Chunking, and the Parts That Break
RAG demos are easy; production RAG is not. The full pipeline, the chunking and retrieval decisions that decide quality, and the failure modes nobody warns you about.
Server Actions in Next.js 16: Forms, Mutations, and the Pitfalls
Server Actions make mutations feel native in the App Router, until security and revalidation bite. Here is how I use them in production, and what to avoid.
Choosing a Vector Database in 2026: pgvector, Pinecone, Qdrant, and When Postgres Is Enough
You probably don't need a dedicated vector database. A decision framework for pgvector vs Pinecone/Qdrant, index types, and the scale where Postgres stops being enough.
React Server Components: A Mental Model That Finally Makes Sense
Server Components confuse even experienced React devs. Here is the mental model that makes the server/client boundary obvious, and the mistakes to avoid.
Background Jobs at Scale: Queues, Workers, and Idempotency
Moving work off the request path is easy; doing it reliably is not. Queues, retries, idempotency, and the failure modes that bite SaaS apps in production.
Structured Outputs and Tool Calling: Making LLMs Reliable
Parsing free text out of an LLM is a bug waiting to happen. How to get typed, schema-valid output and wire up tool calling so the model can act, safely.
Next.js 16 Caching, Demystified: use cache, PPR, and Revalidation
Next.js caching has burned every team I have worked with. Here is how the Next.js 16 model, use cache, cacheLife, cacheTag and PPR, actually works.
Kubernetes for Developers: The 20% You Actually Need
You can be productive on Kubernetes without learning all of it. The handful of objects and commands that cover almost everything, and when not to use k8s at all.
Discriminated Unions: The TypeScript Pattern I Reach For Most
Discriminated unions turn impossible states into compile errors. The single TypeScript pattern that has removed the most bugs from my code, with real examples.
LLM Agents That Actually Work: Tools, Loops, and Guardrails
Most 'agents' should have been a function call. When you genuinely need an agent loop, how to build one that is safe, bounded, and doesn't burn your budget.
You Probably Do Not Need useEffect: Patterns That Replace It
Most useEffect calls I review are bugs waiting to happen. Here are the patterns that replace effects for derived state, data fetching, and event logic.
Reading EXPLAIN ANALYZE: A Practical Guide to Postgres Query Plans
EXPLAIN ANALYZE is the most useful Postgres tool most developers can't read. Here is how to interpret a query plan and find the node that is actually slow.
Evaluating LLM Apps: How to Test Something Non-Deterministic
You can't unit-test a probability distribution with assertEquals. How to build evals, golden datasets, and LLM-as-judge scorers that catch regressions before users do.
TypeScript Generics, From Confusing to Comfortable
Generics are where TypeScript stops being JavaScript with types. A practical, example-first guide to generic functions, constraints, and inference.
Streaming and Suspense in the App Router: Faster Perceived Loads
Streaming lets the App Router send HTML before the data is ready. How Suspense, loading.tsx, and parallel fetching cut perceived load time without rewrites.
Stop Throwing Strings: Typed Error Handling in TypeScript
try/catch loses type information the moment an error is thrown. Here is how I handle errors in TypeScript with typed errors and Result types.
Cutting LLM Cost and Latency in Production: Caching, Routing, and Streaming
LLM bills scale with traffic and latency kills UX. The caching, model-routing, and streaming tactics that cut both, with the tradeoffs spelled out.
Connection Pooling in Serverless: Why Postgres Falls Over and How to Fix It
Serverless plus Postgres is a footgun: every function instance opens connections until the database collapses. Here is how pooling actually fixes it.
The OWASP Top 10 for Full-Stack Developers in 2026
The OWASP Top 10 is still the baseline for web security. A full-stack, code-first walk through each risk and the concrete fix in a modern JS/TS app.
Do You Actually Need Redis? Caching Decisions for Real Apps
Redis gets added to architectures by reflex. Here is a senior dev decision framework for when a cache earns its place, and when Postgres is enough.
Securing LLM Applications: Prompt Injection and the OWASP LLM Top 10
Prompt injection has no clean fix, and treating model output as trusted is how data leaks. A practical security guide for LLM apps using the OWASP LLM Top 10.
Stripe Billing for SaaS: Subscriptions, Webhooks, and the Edge Cases
Wiring Stripe for a SaaS looks easy until proration, failed payments, and webhook ordering hit. Here is how to build billing that survives production.
Web Caching Explained: Browser, CDN, and Server
Most web performance wins are caching wins. A clear map of the layers, browser, CDN, and server, with the Cache-Control directives that actually matter.
Drizzle vs Prisma in 2026: Which TypeScript ORM Should You Choose?
A senior engineer's honest 2026 comparison of Drizzle and Prisma: type-safety, edge cold starts, migrations, relational queries, and a clear pick-this-if framework.
PostgreSQL Indexing Explained: Which Index Type to Use and Why
A practical guide to PostgreSQL index types — B-tree, GIN, GiST, BRIN, partial, composite, and covering indexes — and exactly when to reach for each one.
Next.js 16 Authentication with WorkOS AuthKit: A Practical Guide
A practical guide to wiring WorkOS AuthKit into a Next.js 16 App Router app: middleware, async cookies, protecting Server Components, and a security checklist.
npm Supply Chain Attacks: How to Protect Your Codebase in 2026
npm supply chain attacks are now relentless. Here's the practical, senior-engineer playbook to harden your install, CI, and build pipeline in 2026.
Designing a Permissions System: RBAC, ABAC, and What Actually Ships
Authorization is where SaaS codebases rot. A practical guide to RBAC vs ABAC, multi-tenant permissions, and enforcing them without scattering checks.
Managing State in React in 2026: Server-First, Then Zustand
Half the state you used to keep in React now lives on the server. A 2026 decision framework: server data, URL state, local UI state, and when to reach for Zustand.
JWT vs Session Cookies in 2026: Stop Getting Auth Wrong
The JWT-everywhere trend caused a decade of broken auth. Here is the honest tradeoff between stateless tokens and session cookies, and what I reach for.
Blue-Green and Canary Deployments: Shipping Without Fear
A deploy that can't be rolled back instantly is a deploy you're afraid to make. Blue-green, canary, and the automated rollback setup that removes the fear.
Rate Limiting Strategies for APIs: Token Bucket, Sliding Window, and Where to Enforce It
Rate limiting is your API seatbelt. A practical comparison of fixed window, sliding window, and token bucket, with where in the stack to enforce each.
Zod in Production: Validating at the Edges of Your App
Types vanish at runtime, and external data lies. How I use Zod to validate at every boundary, env vars, API input, webhooks, so bad data fails loudly and early.
GitOps with Argo CD: Declarative Deployments Done Right
GitOps makes your cluster match git, automatically, and tells you when it drifts. How Argo CD works, why pull beats push, and how to roll back with a git revert.
REST vs GraphQL vs tRPC in 2026: Choosing an API Style
REST, GraphQL, and tRPC solve different problems. A senior dev framework for picking an API style based on your team, clients, and constraints.
Audit Logs for SaaS: What to Capture and How to Store It
Audit logs are a trust feature, not just a compliance checkbox. What to capture, how to keep them tamper-evident, and where to store them without slowing writes.
A Production Node.js Dockerfile That Is Not 1.2GB
Most Node Dockerfiles ship a gigabyte of junk and rebuild everything on a one-line change. Here is a lean, fast, secure multi-stage build I actually use.
Zero-Downtime Database Migrations: A Playbook
A bad migration can lock your busiest table at the worst time. The expand/contract playbook for changing a Postgres schema with zero downtime.
Cloud Cost Optimization: Where the Money Actually Goes
Cloud bills balloon in predictable places. A pragmatic FinOps guide to finding the waste, idle compute, egress, NAT, zombie resources, without crippling the team.
Secrets Management: Stop Shipping API Keys in .env
A .env file is where secrets go to leak. The progression from .env to platform vars to secret managers to OIDC, and how to stop committing keys for good.
GitHub Actions Patterns That Save Your Team Hours
CI that is slow, flaky, or insecure taxes every PR. Here are the GitHub Actions patterns I use for fast, cacheable, least-privilege pipelines.
Observability for Developers: Logs, Metrics, and Traces
You can't fix what you can't see. A developer's guide to structured logs, the metrics that matter, and distributed tracing with OpenTelemetry.
Webhooks Done Right: Delivery, Retries, and Verification
Webhooks look trivial until events arrive twice, out of order, or not at all. How to build both sides, signing, idempotency, retries, so nothing is lost.
Building a SaaS That Scales: The Architecture Patterns Senior Engineers Rely On
Battle-tested SaaS architecture patterns from 17 years in production: multi-tenancy, modular monoliths, async work, caching, DB scaling, and safe rollouts.
TypeScript Utility Types You Should Actually Know
TypeScript ships a toolbox of utility types that delete boilerplate. The ones I use every week, with the real scenarios where each one earns its place.
Core Web Vitals in 2026: What Actually Moves the Needle
Most Core Web Vitals advice is noise. Here is what actually improves LCP, INP, and CLS on real sites, measured rather than guessed.
Infrastructure as Code with Terraform: A Pragmatic Start
Click-ops doesn't scale and doesn't review. A pragmatic introduction to Terraform, state, modules, and the gotchas that bite teams adopting IaC.
Postgres Full-Text Search vs a Dedicated Search Engine
Before you add Elasticsearch, check whether Postgres already does the job. Full-text search in Postgres, where it shines, and the signals it's time to move on.
CORS, CSRF, and the Same-Origin Policy, Explained
CORS, CSRF, and the same-origin policy get confused constantly. They solve different problems. A clear, code-first explanation of what each one actually does.