← All writing
Craft · · 9 min

The third proposal

On reading being harder than writing, the six years of undocumented fixes that look exactly like cruft, and how to argue the point without becoming the person who says no to everything.

Culture Agency

A proposal landed in August to rebuild a client platform from scratch. It’s a good document, written by somebody sharp who has been inside that codebase for five months and is not enjoying it, and every criticism in it is accurate.

Then I went digging in the shared drive for what the platform had originally cost to build, and found two more of them.

shared drive, oldest first
Rebuild recommendation.docx        five years ago
Platform rebuild options.docx      two years ago
platform-rewrite-proposal.md       last month
Text

Same client, same system, same conclusion, three different authors, and not one of the three documents mentions the others. The oldest predates me working here. It’s dated about a year after the platform went live, which is its own small comedy.

That pattern is the part I keep turning over, because when capable people independently arrive at the same conclusion, either the conclusion is correct or something is manufacturing it. I think it’s mostly the second. Joel wrote the canonical version of this in 2000 and it hasn’t stopped happening once in the 23 years since, so evidently being told doesn’t help much. Understanding the mechanism well enough to talk about it without becoming tedious helps a little.

Write-only memory

Reading code is harder than writing it, and honestly that’s most of the whole thing.

When you read somebody else’s system you’re reconstructing intent out of artifacts, and every decision arrives with its reasoning stripped off. When you write, the reasoning is just present, effortlessly, because it’s yours and it’s happening right now. I wrote up the order I read an unfamiliar codebase in years ago, and went back to it later for the part I’d left out, which is that the difficulty never really goes away, it just stops being alarming.

So the existing system always feels worse than the imagined one, and that feeling is a fact about the reading instead of a fact about the code. Code I wrote myself 18 months ago feels like this. I have proposed rewriting my own work, out loud, to people who knew perfectly well I’d written it 🫣

You’re comparing a known mess against an imagined clean thing. All of the old system’s complexity is visible, because it’s sitting in front of you as evidence. None of the new system’s complexity is visible, because nobody has discovered it yet. It will turn out to be about as complex, because most of the complexity is coming from the problem instead of from whoever typed the code, and the second system is famously where that gets taught. I once put an afternoon on adding one field to a product and lost four days to a data model, and that wasn’t the previous developer’s fault either.

And rebuilding is more fun. Nobody says that part out loud in the meeting and tbh I think it does a lot of the work. Starting from scratch is enjoyable, it reads well on a resume, and there’s a demo at the end of it. Maintenance is archaeology, and the deliverable is a system that still works, which nobody photographs.

The leaflets

The strongest argument against is also the hardest one to make out loud, because it comes out sounding like sentimentality.

A system that’s been in production for six years contains six years of accumulated response to reality, which is to say hundreds of small corrections for cases nobody wrote down: the customer whose zip code has no space in it, the supplier whose feed sends dates in a different format one day a week, the payment provider’s sandbox that behaves differently from live, the redirect that exists because somebody printed a URL on a run of leaflets.

Every one of those looks like cruft. Every one of them looks exactly like the kind of thing a rebuild would tidy up on its way past. And every one of them is a bug a real customer hit, reported, and then waited on while somebody spent an afternoon figuring out what was different about them and fixed it, at a cost, once. (None of those six years were mine, incidentally. So I’m defending work I didn’t do, which is either a point in my favor or evidence that I’ve gotten romantic about a codebase. Take your pick!)

Rewriting means finding all of them again, in production, by the same method, which is a customer hitting one and calling in.

That’s the last 20% everybody talks about, and it isn’t 20% of the features. It’s every behavior in the system nobody wrote down. There’s no document you can read to shortcut it, because if the document existed then this wouldn’t be the problem in the first place.

So when is it right?

I’d have no credibility on any of the above if I didn’t mean this part. I’ve argued for a rebuild myself twice.

When the platform is genuinely dead. A runtime with no security patches and no upgrade path, a framework whose maintainers have wandered off to other things. At that point you aren’t choosing whether, you’re choosing when, and the useful conversation is about the date.

When it’s small enough to be wrong about. This is the single biggest predictor of how it goes and it gets underweighted every time. A rebuild you can finish in five weeks is a different animal from one estimated at nine months, not because the work differs in kind but because the five-week version can go badly and everybody survives it. Nine months can’t.

When the requirements have actually changed. If the business now does something structurally different then you aren’t rebuilding the same thing at all, and nearly everything I said above stops applying, because those accumulated edge cases describe a problem you no longer have. Moving a store off a server we controlled was roughly this shape. It got sold internally as an infrastructure decision when it was really a decision about which problems we’d be allowed to have.

When it’s demonstrably cheaper than carrying on. Sometimes it is! Just make somebody show the math instead of the feeling.

Yes, and

The practical part, because “this is usually a bad idea” is not a sentence you can say in a meeting without turning into a character.

Agree with the goal and question the mechanism. The goal is never “rewrite it.” The goal is “this is too hard to change and it’s slowing us down,” which is true, and which everybody in the room already agrees with. So what specifically is hard? Which changes take longest? About 20 minutes later the two of you are looking at a list instead of at each other.

Ask for a range and for the reason it’s that wide. A rebuild estimate is the widest one anybody will ever hand you, and the honest version of it clarifies things in a way one number never does, which I went on about at length in 2018. Asking what the pessimistic case is, and what makes it pessimistic, isn’t hostile. It’s the same question I’d want asked about my own numbers.

Ask what happens if it takes twice as long. Not as a gotcha, as planning. Every rebuild I’ve watched has run at roughly double, and if double is fatal to the business then the plan has no slack in it, which is worth finding out beforehand instead of afterward.

Propose replacing one piece instead. The worst part of the system, behind the same URLs, in a few weeks. If it goes well you have evidence and momentum and a pattern you can repeat. If it goes badly you’ve spent a few weeks and learned the thing the nine-month version would have taught you in month seven, at roughly a twentieth of the price.

And be willing to lose the argument. Whoever wrote that proposal has spent five months inside the code and has a far better map of which parts hurt than I do. They might know something I don’t, and if I’ve already decided then the conversation is theater and they can tell it’s theater.

The boy who cried no

The part underneath all of this took me about eight years to notice.

The developer who objects to everything stops being heard. Not because they’re wrong, they’re usually right more often than average, but because “they’ll say no” becomes a known quantity and people start routing around it. They stop asking. Decisions get made in a channel you aren’t in, and you find out afterward.

So opposition is a budget and it’s smaller than anybody wants it to be. Spend it on the ones that matter, say yes to the things you’re merely unsure about, and be visibly delighted when you turn out to be wrong, because the times you’re wrong are what pay for the times you’re right. I wrote a whole post about refusal a couple of years ago and left this part out of it entirely, which I assume is because I hadn’t figured it out yet.

Five weeks instead

With the August proposal we actually took the worst part of the system, which is the ordering flow, and give it five weeks behind the existing URLs. It went fine. It also turned up two behaviors nobody on the account knew were there, both of which we’d have found much later and much more expensively.

Nobody has raised the full rewrite since. I don’t think that’s because anybody won an argument, either. It’s that “this is too hard to change” stopped being true of the part that was actually hurting, and all three of those documents were downstream of that feeling instead of downstream of the code.

Which leaves me not really knowing what to do with the other two. Filing them somewhere the next person will find them feels like the right instinct, and also a little like leaving a note saying we considered this three times and decided against it, which isn’t what happened. We didn’t decide against it three times. We ran out of appetite three times. That’s a different thing to write down.

Read similar posts
9 min

The loan with no lender

A ticket I wrote in 2022 asking for a few days to clean up the checkout templates has now outlived 2 project management tools, and in 3 years not one person has argued against doing it.

8 min

Somebody typed it into a text box

I spent three days on a maintenance retainer taking things out of a client's site, and the last thing on the list turned out to be holding up the one page the whole business hangs on.