Pay down tech debt aboard the Ship of Theseus
Tags: management, codequality, architecture
Published: 2019-08-31 04:25:32

---
title: Pay down tech debt aboard the Ship of Theseus
published: true
description: Paying down tech debt by replacing rotten code, class by class
series: Technical Debt
tags: management, codequality, architecture
---

A year ago I learned of the thought experiment “The Ship of Theseus” and, particularly, how it might apply to software development.

In a nutshell, the Ship of Theseus refers to an old wooden sailing ship sailed by Theseus. Over the course of its voyage, every piece of the ship was replaced at least once. The thought experiment asks the question “Is it still the same ship?”

This concept, applied to software architecture, posits that you can replace an application method by method, class by class over time and have what is effectively a new version of that application. Of course, the answer to the thought experiment, when applied to software is simple: The binaries and code are different, leading to a different form and factor, so a software change results in a slightly different new ship at the expense of the old ship, in this author’s opinion.

But that’s the point — we don’t want to replace a buggy or convoluted method or class with an identical version — as we work, we want to clean up legacy code, expand tests, improve performance, modernize the technology stack, etc. Modifying code can be as much about cleaning up dust and tightening the design as it is about fixing a bug or adding a new feature.

The ultimate dream in these systems is that we replace a system bit by bit until it resembles the sleek yacht we want it to be, not the rotten old barge we started with.

This has a number of benefits. One, instead of selling a massive rewrite, we improve the software by following the boy scout principle — by making our campsite (the code we change) better than when we started working with it — every time we make a code change.

Secondly, the risk of changing an individual “plank” of an application is much lower than changing a layer, subsystem, or a complete rewrite of the application. Additionally, the testing load our changes cause is minimized and often identical to the footprint already under test.

Third, the intimidation factor and analysis paralysis behind redesigning a system are converted to something much more actionable — the simple problem of taking a small “plank” or two of an application and improving or replacing it.

This is not without downsides, however. Under this mentality of slowly replacing and modernizing planks, it can be harder to make broader changes such as moving to a new technology or hosting platform or making significant architectural changes. It’s certainly possible (and I’ve done it a number of times over the past year), but it’s harder.

Additionally, by progressively cleaning up code as you’re working with it, you are magnifying the chance of introducing bugs with each change. Unless you have a good degree of covering tests when you start, you are risking only partially replicating the behavior of the “plank” you are replacing. By extension, following this mentality places a heavier burden on both quality assurance and developers writing unit tests.

This additional time you spend on every feature / fix is not without cost either as it slightly slows down your velocity when developing. Still, this is much easier to work around than the much larger delays of rewriting an application from the ground up.

Perhaps the greatest downside of following the Ship of Theseus approach instead of rewriting an application in need of attention is the factor of software design. A common adage in performance improvement is that design is typically the chief cause of poor performance. By replacing the parts of an application without reshaping its overall behavior, it removes our chance to address design-level issues.

That said, after 12 months of following this voyage, I am happy to report that the seas are calm and blue. Technical debt is being paid down in the most critical areas (because that’s where the bugs tend to be), being intentional about quality has minimized the risks inherent to replacing larger pieces of the application, and this approach has validated itself as a way of stewarding applications towards maturity and stability. Sometimes you need to "cheat" a little and replace an entire main mast or rudder at a time, but generally speaking, the principle is sound and worth exploring.