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 →Every developer inherits code they didn't write, without tests or documentation. How to understand an unfamiliar codebase quickly, add safety nets, refactor in small steps and avoid the big rewrite trap.
Sooner or later, you'll inherit a codebase that nobody fully understands: no tests, outdated dependencies, mysterious workarounds and a comment that says // don't touch this. That's legacy code, and working with it is a core professional skill. Most of the code in the world is legacy code, and most of it is running something important.
Resist the urge to "clean it up" on day one. Weird code often exists for reasons you can't see yet.
Follow the README if there is one. Note every step that's missing or wrong, then fix the README. You've already improved the project. (Writing good docs.)
Start where requests enter (routes, controllers, main functions, scheduled jobs) and follow one important flow end to end, like "user places an order." Draw it out on paper or in a diagram tool. A single traced flow teaches you more than skimming a hundred files.
git log --follow -p path/to/file.js # how did this file evolve?
git blame path/to/file.js # who changed each line, and in which commit?
git log -S "calculateDiscount" # when was this text added or removed?
Commit messages and linked tickets often explain the strangest-looking code: "workaround for supplier API returning negative stock." Now the weird line makes sense.
If anyone who worked on it is around, a 20-minute conversation can save days. Ask about the scary parts, known bugs and the things nobody dares to change.
Changing untested code is gambling. Before modifying important behaviour, add characterization tests: tests that capture what the code currently does, not what it should do.
test('current discount behaviour for gold members over 100', () => {
// We don't know if this is "right", but it's what production does today.
expect(calculateDiscount({ tier: 'gold', total: 150 })).toBe(22.5)
})
These tests let you refactor with confidence: if behaviour changes unexpectedly, you'll know immediately. Integration tests around key endpoints are often the fastest way to get broad coverage on a legacy app. (Testing strategy.)
A seam is a place where you can change behaviour without editing much code: a function parameter, an interface, a configuration value. Introducing seams, for example passing a dependency in instead of creating it inside a function, lets you test and replace parts gradually.
"Let's just rewrite it from scratch" is tempting and frequently disastrous. Rewrites take longer than expected, the old system keeps changing in the meantime, and years of hidden knowledge (all those weird workarounds) get lost and rediscovered as bugs.
Prefer the strangler fig approach:
Each step delivers value and can be rolled back. (Architecture choices.)
Legacy projects often run on very old framework versions. Upgrade incrementally: one major version at a time, following official migration guides, with tests run after each step. Jumping several major versions at once multiplies the risk. (Dependency management.)
Legacy work can feel thankless. Remember that this code is valuable: it's earning money or serving people right now. Make progress visible (fewer bugs, faster builds, better docs), and celebrate small improvements. They add up.
Code that's hard to change safely, often older, written by people no longer around, and usually lacking tests or documentation, but still providing real value in production.
Get it running, trace one important flow from entry point to database, read Git history for context, ask people who worked on it, and write down what you learn.
Usually not. Incremental refactoring with tests, or replacing pieces gradually with the strangler fig pattern, is far less risky than a full rewrite.
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 →