Blog
Posts on software development, careers, and craft.
Trend I'm seeing: AI app generators are a sales funnel
Trend I'm seeing: AI app generators are a sales funnel. -Chef uses Convex. -V0 is optimized for Vercel. -Lovable uses Supabase and Clerk. -Firebase...
Problem: Choosing technologies is hard
Problem: Choosing technologies is hard. Solution: Be honest about tradeoffs. Tell me when it's a good fit, and when it's not. Example: @livestorede...
Prediction: AI will replace most web app UIs
Prediction: AI will replace most web app UIs. Why? Because nobody wants to use your UI. They just want results. AI provides results without requiri...
The local-first / sync engine ecosystem is far bigger than I realized
The local-first / sync engine ecosystem is far bigger than I realized. There are DBs / BAAS providers that provide real-time sync like Supabase, Fi...
Confession: The last year has been hard for me
Confession: The last year has been hard for me. AI has me feeling uneasy about my software development career. Part of me thinks "Why bother if an...
Problem: You want to run a "real" DB in the browser
Problem: You want to run a "real" DB in the browser. Two solutions: 1. Run SQLite in the browser via sql.js 2. Run Postgres in the browser via PGLi...
The weird thing about 2025
The weird thing about 2025? Memorizing is rarely useful now. Sure, Google and Stackoverflow made memorization less useful many years ago. But AI ma...
Problem: ESLint is slow on large projects with complex rules
Problem: ESLint is slow on large projects with complex rules. Solution: Use a fast Rust-based alternative. Two options: 1. Biome (which also replac...
This can’t be real The sign of a failing organization is when everyone knows...
This can’t be real The sign of a failing organization is when everyone knows it’s wrong but it ships anyway.
"Today's LLMs are like a junior developer"
"Today's LLMs are like a junior developer". Great, that means I can hire a junior dev to help me for just $20/month! The productivity gap between d...
I bought my M1 Max Macbook Pro in October 2021
I bought my M1 Max Macbook Pro in October 2021. After over 3.5 years of HEAVY use, the battery finally reports "Service recommended". Max capacity...
This post by Dan Abramov on "Progressive JSON" helped me understand how...
This post by Dan Abramov on "Progressive JSON" helped me understand how RSC's streaming works, and its over-the-wire format. It's like the differen...
I love Tanstack Router's type safe search params
I love Tanstack Router's type safe search params. ✅Autocomplete support ✅Zod schema validates params ✅Param validation logic is centralized with th...
In a React app, declaring an ErrorBoundary at the root is common, but rarely...
In a React app, declaring an ErrorBoundary at the root is common, but rarely sufficient. I also wrap routes and page sections in an ErrorBoundary t...
TypeScript syntax is often inspired by JavaScript
TypeScript syntax is often inspired by JavaScript. In some cases, TS even uses the exact same syntax as JS for a different purpose. The difference?...
Seeing these two side-by-side in my timeline really sums up the current...
Seeing these two side-by-side in my timeline really sums up the current schism on the upcoming impact of AI.
My favorite things about Europe vs the USA: 1
My favorite things about Europe vs the USA: 1. Better transportation infrastructure - Trains, trams, buses, rental scooters, bikes, wide sidewalks,...
The rumors were true - Remix is coming back and it’s not using React
The rumors were true - Remix is coming back and it’s not using React. Instead, it’s a fork of Preact! The new Remix is aiming for lightweight, modu...
Problem: I want to ensure my switch statement handles all potential cases
Problem: I want to ensure my switch statement handles all potential cases. Solution: Use TypeScript's never type. Here's how:
For me, LLMs work quite well as “glorified autocomplete”
For me, LLMs work quite well as “glorified autocomplete”. But so far I’m skeptical about using them for agentic workflows - feels like a house of c...
Problem: You have complex logic for narrowing a TypeScript type that you'd...
Problem: You have complex logic for narrowing a TypeScript type that you'd like to reuse in a few spots. Solution: Consider an assertion function.
Woah, @tan_stack just added Tanstack DB, a sync engine that works with...
Woah, @tan_stack just added Tanstack DB, a sync engine that works with Tanstack query. It adds: -Live queries -Fine grained reactivity (to minimize...
Local-first web apps already have a big ecosystem
Local-first web apps already have a big ecosystem. Here’s what I’ve found so far. What else should I try? CRDT libraries: - Automerge - Purely clie...
Four related web app problems: 1
Four related web app problems: 1. If I’m temporarily offline, I still want the app to be fast and reliable. 2. If my work conflicts with someone el...
Just realized Vitest now has built in support for testing TypeScript types
Just realized Vitest now has built in support for testing TypeScript types. How? It includes expect-type now by default. Handy. Example:
Vibe coding is a leaky abstraction
Vibe coding is a leaky abstraction. A leaky abstraction means I often have to understand the underlying implementation (in other words, the code) t...
Vibe coding is the modern version of copy/pasting code you don’t understand...
Vibe coding is the modern version of copy/pasting code you don’t understand from Stack Overflow. It seems fine at first, but at some point you need...
How to write bad software: Standardize nothing
How to write bad software: Standardize nothing. Let each developer pick their own conventions, patterns, languages, and libraries. Ignore quality....
Just converted a small test suite from Vitest to bun:test
Just converted a small test suite from Vitest to bun:test. Bun is about 2x faster. Vitest: 575ms Bun: 273ms And since bun:test syntax is similar to...
Oooof
Oooof. This quote hits. “I think people get old when they stop talking about the future. If you want to find someone’s true age, listen to them. If...