Every Rewrite Was Going to Be the Last
I rewrote the same core component of my system three times. Not patched, not refactored — rebuilt, from close to scratch, three separate times. And every single time, I was certain that this version was the real one: the clean, correct architecture I would finally build on for good.
I was wrong three times in a row, with full confidence each time. The being-wrong was annoying. The full confidence is the part I actually want to talk about, because it was the same feeling repeating, and I never once recognized it as a warning.
”This time it is right”
There is a specific high that comes with a fresh rewrite. The old code is a mess — you can see every compromise, every patch, every place where reality forced you to bend. The new design, sitting clean in your head, has none of that. It is elegant. It is obvious. It is clearly better.
So you tear down the old thing and build the new one, and for a while it feels like progress, because the new code really is cleaner. Then you start using it, and the bending begins again, and some time later you are standing in front of a second mess, feeling the exact same itch to start over.
Why the rewrite always looks better
The comparison is rigged, and it took me three rounds to see how. When you judge a rewrite, you are comparing a fresh, idealized design against an old one that has spent months absorbing the friction of actual use. Of course the new one looks better. It has not met any of the problems yet.
The old code looks ugly not only because it was imperfectly designed, but because it is wearing its experience. Many of the awkward branches in it are scars from real edge cases — things the world threw at it that the clean new design has simply never had to survive. You are not always comparing a good design to a bad one. Often you are comparing a veteran to a recruit and being impressed by how unscarred the recruit is.
The cost I kept paying
Each rewrite threw away more than code. It threw away knowledge — the accumulated, hard-won handling of all the strange situations the old version had quietly learned to deal with. And because that knowledge lived in the code rather than in my head, rebuilding meant rediscovering it: hitting the same weird cases again, debugging the same problems again, paying a second and third time for lessons I had already bought.
There is a particular kind of exhaustion in fixing a bug and realizing you have fixed this exact bug before, in a version of the code that no longer exists. That was much of my third rewrite — slowly re-earning the knowledge I had thrown away in the second.
The thing underneath: premature abstraction
The real engine of all this rebuilding was a mistake in how I thought about design. I kept rewriting because I kept trying to find the right structure before I actually understood the problem. I treated the correct abstraction as something I could sit down and design correctly up front, if I were only clever enough.
In my experience, useful abstractions were rarely designed correctly up front; they were earned through use. They come from living with the problem long enough to learn where it really bends — which parts vary and which stay fixed, where the true seams are. Designing it fully in advance is guessing, and each rewrite was just me guessing again, slightly better informed, but still well before I had learned enough to guess well.
When a rewrite is actually worth it
I do not want to claim rewrites are always wrong. Sometimes a design genuinely cannot stretch to where you need it to go, and evolving it in place would cost more than starting fresh. The distinction I eventually learned to draw is about the source of the urge.
A rewrite driven by genuine new understanding of the problem — “I now see that this thing I treated as one concept is really two” — is often real progress. A rewrite driven mostly by discomfort with the current code — “this is ugly, I want something clean” — is more often motion wearing a fresh coat of paint. The first is more likely to produce meaningful structural progress. The second mostly changes how you feel about it for a few weeks, until the new thing gets ugly too.
The fix: bend it in place, until you truly can’t
The habit I built was to demote rewriting from a first instinct to a last resort. Faced with a structure that annoys me, I now try to change it in place first — reshape it, refactor it, let it keep its accumulated knowledge while I move it toward what I need. I try to reserve a full rebuild for when the existing structure genuinely fights every attempt to evolve it, not merely when it offends my sense of tidiness.
Much of the time, the discomfort that makes you want to start over is information, not a verdict. It is the code telling you where it hurts — and that is exactly the knowledge you would throw away by rebuilding from scratch.
The certainty was the tell
The thread running through all three rewrites was not bad code. It was a feeling: the clean, total certainty that this time I had it right. “This is the final architecture” was the sentence I said, in effect, before each one. That a sentence keeps turning out wrong should lower your trust in it, and slowly it did.
Strong certainty about a design you have not yet stress-tested is not the same as insight. It is often just the absence of the scars that would have taught you otherwise — the comfortable confidence of a plan that has not yet met the world. These days, when I feel completely sure a new design is the final one, I try to treat the feeling itself as a hint that I may not have lived with the problem long enough to be trusted with that opinion.
— No signals, no returns, not investment advice.