What Happens When You Type a URL and Press Enter
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →Schema changes cause more scary deploys than almost anything else. How migrations work, the rules that keep them safe, and the expand-and-contract pattern for changes with zero downtime.
Changing a database schema in production is one of those things that's routine right up until it isn't. A column rename takes down the app for a minute. An index build locks a table during peak traffic. Two developers' migrations collide. Most of these problems are avoidable with a few habits.
A migration is a versioned, ordered script that changes your database schema: create a table, add a column, add an index. Each one is committed to Git alongside the code that depends on it.
migrations/
20260301_create_users.sql
20260308_create_orders.sql
20260315_add_status_to_orders.sql
A migration tool (built into Laravel, Django, Rails, Prisma, Drizzle, Knex, Flyway and many more) keeps a table in the database recording which migrations have run, and applies new ones in order.
The result: every environment (your laptop, a teammate's laptop, staging, production) can reach exactly the same schema with one command.
Once a migration has run on another machine or in production, it's history. Editing it means environments silently diverge. Need a change? Write a new migration.
No "quick fix" in the production console. If it isn't in a migration, the next environment won't have it, and nobody will know why things differ.
One logical change per migration. Small migrations are easier to review, quicker to run and easier to reason about when something fails.
Backfilling millions of rows inside a schema migration can lock tables and time out deploys. Run large data updates as separate, batched scripts or background jobs. (Background jobs.)
Especially anything that drops or transforms data. And make sure you know how to restore. (Backup strategy.)
A migration that takes 50 milliseconds on your 100-row dev database might take 20 minutes on 50 million rows. Test heavy migrations against a copy of production-sized data.
During a deploy, old and new versions of your code often run at the same time. A migration that removes or renames something the old code uses will break it.
The fix is to split risky changes into backwards-compatible steps. Renaming name to full_name:
full_name (nullable). Deploy.name values into full_name, in batches.full_name. Deploy.name, then drop it in a later migration.More steps, yes, but no moment where the running code and the schema disagree.
CREATE INDEX CONCURRENTLY in PostgreSQL, so writes aren't blocked. (Indexing guide.)NOT NULL column: add it as nullable or with a default first, backfill, then add the constraint. (Recent PostgreSQL versions add a column with a constant default quickly, without rewriting the table.)lock_timeout so a migration fails fast instead of queueing behind traffic and blocking everything.Many tools support "down" migrations to reverse changes. They're handy in development. In production, reversing a migration that dropped data can't bring the data back. Rolling forward with a new fix is usually safer, and a tested backup is the real safety net.
A versioned script that changes the database schema in a controlled, repeatable way, applied in order by a migration tool so every environment ends up with the same structure.
Only if it has never run anywhere except your own machine. Once it's been applied elsewhere, create a new migration instead.
Use expand and contract: add the new column, write to both, backfill existing data, switch reads to the new column, then drop the old one in a later deploy.
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →There are dozens of HTTP status codes, but about fifteen cover nearly everything. What each one means, when to return it, and the mix-ups (401 vs 403, 301 vs 302) that confuse everyone.
Read →A, AAAA, CNAME, MX, TXT and NS records in plain English, why DNS changes seem slow, and the troubleshooting commands that tell you what is really going on.
Read →