Part of a codebase I work on suffers from a quality hotspot where half of it got rewritten so it was "nice" and the other half was so hard to reason about it stopped them dead in their tracks and it has persisted in this Frankenstein state for a reasonably long time.

On a project with an external deadline this year I had to make some modifications for compliance reasons. It was very painful. The parts that are rewritten are conceptually a lot simpler. They fit in my head. The parts that didn't get rewritten have incredibly high cyclomatic complexity and really don't fit in my head.

I want to refactor it so it's nice.

I also don't want to introduce lots of bugs nor push out deadlines.

A decision was made upfront that we would try to extract this component out of the main system, so it could be independently modified and deployed, as the capability to do so would actually provide decent business value. Sadly this particular decision didn't turn out well, and really, really pushed the deadline. Ultimately we spent about 70% of the engineering effort on the project trying to extract some functionality. I'd say about 10% of is actually extracted. So now it's even more of a Frankenstein split across 2 codebases now.

The project is coming to a close, and we will have a clean up session early next year. Our next project is related to this particular component also, so there is scope for us to be making changes to it.

The goal is to make this component more stable for customers, and safer to deploy.

I think a big bang rewrite is 80% likely to fail. I strongly suspect it's actually worth succeeding at though. I'm trying to chart a path through to a successful reworking of this critical subsystem. The first failure was quite humbling, but not unexpected.

Probably the best move I made on this project was getting good logging in place so I can tell exactly what it does, and made it a lot easier to reason about whether our attempted extraction does the same thing. Also I've managed to collapse some of the cyclomatic complexity, so it's easier to mentally keep track of as you're trying to read the code. It's still very, very hard to modify the functionality of the old stuff.

My current line of thinking on the best way forward is to abandon the attempted extraction, and repatriate that functionality to the main codebase. Then use characterization/approval tests and finish the original rewrite. I'd be happy to do it piecemeal too, even one ticket a sprint spread out over the course of the year, so long as we actually complete it. In addition I'm planning to try a a tiny, reversible experiment at a different way of extracting it that's much more likely to succeed and deliver the benefits we're looking for while simultaneously charting a way forward on another major piece of tech debt that causes a lot of stability problems. If that experiment succeeds I want to extract all of it that way, and would only attempt to do so once the entire thing was in a reasonable condition first. Getting it all the way to that state would represent a major win in my mind.

It's hard when we can't afford to fail at this, and we're not likely to succeed. Really trying my best to find a viable plan.

At the same time, if it ain't (that) broke, don't fix it right? But that leaves no road to new capabilities for us as a business.

Software is just a hard problem.