March 21, 2023

Leaving No Code Behind: The Hidden Costs of Unfinished Migrations

You start working on a new shiny greenfield project, brimming with excitement and promise. Life is good, and the sun is bright. As time goes on, however, you notice that the once-pristine landscape starts to be tainted by tiny spots of rust.

You realize your initial implementation no longer solves the core problem that well. There has to be a better way. You get your hands dirty, and there you have it. The v2: a sleek, sophisticated design that promises to right all wrongs and scream elegance. Yet, before you clean up every corner of your codebase, new issues arise, leading you down the rabbit hole of perpetual rewrites (v3, v4, ..).

Sound familiar? If you find yourself trapped in this endless cycle, it's time to hit pause. In this post, I'll break down why it's important to complete your migrations rather than leaving behind a tangled web of unfinished work.

The Cognitive Load of Multiple Versions #

When a refactoring project is left unfinished, it usually results in several versions (v1, v2, v3, ..) coexisting in the system. This significantly increases the cognitive load when working with the system, as developers must now consider how each algorithm or decision path works across multiple versions. A fully migrated system, even with less optimal solutions, is easier to maintain and develop than a system with multiple competing versions.

Maintaining Consistency #

One of the biggest challenges in adopting new approaches is maintaining consistency across the codebase. When a new method is implemented in a small fraction of the system, developers must constantly remind themselves and others of the new approach, rather than relying on the natural inclination to follow existing patterns.

Whether you like it or not, a lot of programming is copy-pasting existing working solutions. And that’s a good thing, if done thoughtfully. But this also means that if there are competing approaches to solving the same problem, you are faced with a dilemma: which one to copy – “Do I go with the good ol’ v1? Or maybe I should use v2? Wait, how was v2 really better than v1? And wasn’t a v3 recently pitched in a meeting anyway?”

Achieving 100% migration ensures that everyone understands the current operating approach and makes decisions based on it, reducing cognitive load and improving efficiency.

Overcoming Perfectionism & Acceptance #

A common pitfall in software engineering is perfectionism. The constant pursuit of an ideal solution. It's important to accept that even though v2 may not be perfect, it is still better than the v1 (that's why you did it, right?).

Going all the way to 100% migrated codebase has a side benefit. It exposes you to the full spectrum of related issues, thus deepening your understanding of the core problem. This makes you better equipped to improve the solution further (meaning your potential v3 will be better and reducing the need for a v4).

Building Trust & Track Record #

With every completed migration and refactoring project, you build a strong internal track record and trust. When projects are finished on time and deliver tangible benefits, it fosters confidence in the team's ability to adapt and change. Additionally, articulating the upside of these projects to stakeholders can further solidify support for future migrations.

Conclusion #

In software engineering, the cost of leaving migration and refactoring projects unfinished can be immense. From increased cognitive loads to inconsistent codebases, these hidden costs of unfinished migrations can hinder your team's productivity and erode stakeholder trust.

By prioritizing the completion of migration projects, embracing incremental improvements, and building an internal track record of success, your team will maintain a culture of engineering excellence that enables them to tackle crucial tasks and drive meaningful progress.