Every takeover starts the same way: someone gives us access to a repository they’ve paid a great deal of money for, and apologises for it in advance.
The apology is rarely necessary. Across the rescues we’ve run, the code has almost never been as bad as the person handing it over feared. Something else is usually broken, and it’s usually the same something. More on that below.
Day one: don’t read the code
The first thing we do with an inherited codebase is refuse to read it. Reading is opinion-forming, and opinions are cheap on day one. Instead we try to do the three things the previous team did every day: build it, run it, deploy it.
The README says npm install && npm run dev. The README is lying about something. A version pinned nowhere, an environment variable that lives in a departed engineer’s shell profile, a service that turns out to exist only in production. The distance between the README and the truth is a surprisingly good measure of the last six months of that team’s life.
Then the deploy — which is where the horror actually lives. We have inherited perfectly decent products that could only be released from one specific laptop, by one specific person, who no longer worked there. The code was fine. The company’s ability to change the code was gone. Those are different assets, and the second one is the one you actually paid for.
What the history knows
Once it builds, we read — but we read the commit history before the source. The history is the team’s diary, and it doesn’t know how to lie. Long steady stretches. Then a cluster of 2am commits around a demo date: fix, fix again, FINAL fix. A month where every message starts with temp. Panic leaves timestamps.
The database gets the same treatment. Code can be refactored into telling whatever story the last engineer preferred; the migrations folder remembers what actually happened, in order, including all three attempts at multi-tenancy.
Tests we check for honesty rather than coverage. A suite reporting eighty percent coverage built from assertions that assert nothing is worse than no suite at all, because it’s armour that isn’t. It takes one grep to find out. We’ve stopped being surprised by what it finds.
The memo
Two weeks in, whoever owns the product gets a written memo with three columns — keep, fix, cut — and every part of the system sits in one of them, with reasoning attached. The reasoning is the point. “This is bad” is taste, and taste is how the last rewrite got approved.
Keep is usually the biggest column, which surprises people. The previous team’s odd choice of date library works. Leave it. Rewrites flatter engineers and bankrupt roadmaps; a rescue that quietly becomes a rewrite has simply moved the failure six months to the right. Working code that offends our preferences is still working code.
Cut has to be earned with evidence. Hand-rolled authentication. Credentials committed to the repository in 2024 and rotated never. The four-thousand-line class that every feature must crawl through, where fixing has become more expensive than removing. When we put something here, the memo says what it costs to keep — not that we’d rather not look at it.
Fix is the column with dates on it. The bug that blocks the launch. The infrastructure that pages somebody at 3am. The tests that would have caught the bug that blocks the launch.
The memo belongs to the client either way. Some take it and finish the job with their own team, which is a perfectly good outcome — the audit was the unblocking, not the engagement. If you’re currently staring at one of these situations, we’ve written down how a takeover runs; the short version is audit first, commit after.
After enough of these, the pattern is hard to unsee. The code is rarely as bad as you fear. The deploy process always is.