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 →One missing index can turn a 5ms query into a 5 second one. What indexes are, how to read EXPLAIN, how composite indexes really work, and when an index makes things worse.
Most slow web apps aren't slow because of the programming language or the framework. They're slow because the database is reading far more data than it needs to. Indexes are the single biggest lever you have, and you don't need to be a DBA to use them well.
Think of the index at the back of a book. Without it, finding every mention of "caching" means reading every page. With it, you jump straight to the right pages.
A database index is the same idea: a separate, sorted structure (usually a B-tree) that maps column values to the rows containing them. Without a suitable index, the database does a full table scan, checking every row. On 500 rows that's instant. On 5 million, it isn't.
Start with the columns that appear in:
WHERE clauses you run often: WHERE email = ?, WHERE status = 'active'JOIN conditions: foreign keys like orders.user_idORDER BY on large result sets: ORDER BY created_at DESCusers.email enforces the rule and speeds up lookupsPrimary keys are indexed automatically. Foreign keys are not always. PostgreSQL does not automatically index foreign key columns, while MySQL's InnoDB does. If you're on Postgres, check those join columns.
Never guess. Ask the database how it plans to run your query:
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20;
What to look for:
Seq Scan (Postgres) or type: ALL (MySQL) on a big table: a full scan, usually bad.Index Scan / Index Only Scan / ref: the index is being used.Then add an index and run EXPLAIN ANALYZE again. The before and after numbers make the case for you.
An index on multiple columns is sorted by the first column, then by the second within that, and so on. Like a phone book sorted by last name, then first name.
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at);
This index helps:
WHERE user_id = 42WHERE user_id = 42 ORDER BY created_at DESCWHERE user_id = 42 AND created_at > '2026-01-01'But it doesn't help much with WHERE created_at > '2026-01-01' alone, because created_at isn't the leading column. This is the leftmost prefix rule. A useful default ordering: put columns tested for equality first, then range or sort columns.
You added an index and the query is still slow. Common reasons:
WHERE LOWER(email) = 'a@b.com' can't use a plain index on email. Use an expression index (CREATE INDEX ... ON users (LOWER(email))) or store normalised values.LIKE '%smith' can't use a B-tree index. LIKE 'smith%' can. For real text search, use full-text search features.true rarely helps. The planner may correctly decide a scan is cheaper. (Partial indexes help here: CREATE INDEX ... WHERE is_active = false.)ANALYZE (Postgres) or ANALYZE TABLE (MySQL) refreshes them.Every index:
INSERT, UPDATE and DELETE has to update every relevant index.So don't index everything "just in case." Index for the queries you actually run, and periodically look for unused indexes. Postgres exposes usage statistics in pg_stat_user_indexes.
pg_stat_statements, or your APM tool.EXPLAIN ANALYZE on the worst offenders.EXPLAIN ANALYZE again.Also check you're not running the same query hundreds of times per request. That's the N+1 problem, and no index will save you from it. For the bigger picture, see why is my website slow?
There's no fixed number. If a table is write-heavy and has indexes nobody's queries use, that's too many. Every index should map to real query patterns.
It can. On a large production table, use your database's online option, such as CREATE INDEX CONCURRENTLY in PostgreSQL, so normal reads and writes keep working while the index builds.
Usually, yes, especially in PostgreSQL where it isn't automatic. Foreign keys are used in joins and in cascading deletes, and both get slow without an index.
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 →