DEV Community

Phillip Voyle
Phillip Voyle

Posted on

How to solve 5 kinds of architectural tech debt

Tech debt is a pretty course term
In general it’s technology choices or practices that may have initially allowed your application or architecture to move fast, but that now slow you down

Coming through university I knew about reuse and polymorphism and once I got out into the industry, this was about 2002 I learned about design patterns and started to think clearly about the interactions between those structures that allow reuse, but it wasn’t until a few years later that I started to notice problems around me where rot had started to happen. When I think back, the first commercial position I took they had a pretty clear architecture, exceptionally high staff retention and some really talented programmers and really I didn’t get to see or experience technical debt until I got into the game industry a few years later, where the rush mindset and staff churn play a larger part

There are different kinds of tech debt and they are all caused by different forces in your organisation. Unfortunately it’s not always obvious you’re creating tech debt until you see the friction that a particular solution causes, but 20 years of software architecture and design has taught me that there are some constants

Here are 5 kinds of tech debt and what to do about them

Depreciation

Software doesn’t rot so much as go out of fashion
If you don’t regularly and carefully review the packages, libraries, methodologies, frameworks, runtimes and practices that you depend on you’ll find that one or all of them will become dated. Deprecated packages tend to form islands of supportability and at least some of the time you’ll find that a whole chunk of your system will have to be upgraded to deal with a dependency or platform going out of support. Not dealing with this regularly and repeatedly will require you’ll have to stop work and do an emergency upgrade which could mean months of development work and testing to ensure you haven’t broken anything

To deal with depreciation you need to book regular dependency reviews. Quarterly, biannually, or yearly are pretty good choices and won’t overwhelm your schedule with upgrades

Entropy and Mediocrity

Mediocrity will always tug at an elegant solution and try to pull them back into the mediocre. This often happens when a single developer has to deal with a special case that a common piece of code doesn’t strictly accommodate. Often this looks like the special case being hard coded into that general purpose logic, making it more fragile and specific and reducing its ability to be reused. Reusable logic takes maintenance and boundaries that are regularly enforced

There’s only one way to deal with this, it requires that code is consistently to a high standard. If you allow your expectations and standards to drop you’ll let mediocrity into your code base and that will set a diminishing expectation that will cause your application to rot from the inside. If you’re in charge of code quality, you need to set the bar and hold it high

Dependencies

This is almost a special case of the previous kind of tech debt. Developers will need a part of a library or package and simply add a reference. This may unwittingly cause them to bring in other dependencies that aren’t relevant to that problem. Over time your system will become a web of interconnected and implicit references which will slow builds, and make choices about testing and deployment boundaries difficult or impossible to make. There’s only one way to deal with this and that’s carefully reviewing dependencies, splitting components and intentional re-engineering of code that has a murky reuse proposition.

I’d urge you to decide whether your component is shared or not, and if it is, extract it and make it general purpose

Overengineering

Overengineering is often the response to a difficult problem that is impossible to generalise. The developer attempts to make concessions to the problem space by accepting complexity, and special cases. I’ve been guilty of this several times, and I think the problem here can be summed up by my attraction to problem solving and complexity. Solving hard problems is fun and challenging, but beware. The more complex your solution has to be to solve your general problem, the more difficult it will be for other developers to pick it up. If other developers can’t pick up your work there will be friction that makes change hard, and it will get you stuck on a project that you’ll never be able to progress from. This general solution will also attract entropy and over time will collect nicks, cuts, hacks, and open gashes.

To avoid overengineering, shrink your problem space and allow yourself and your team to create a second solution for a special case

Manual Processes

Personally I think you have to face that not everything can be automated, and in this day and age that’s a good thing for us organic life forms. But there are a great many processes that if you spent a bit of time looking at how much you do it and how much variation there is in the process then you’ll find that it’s actually pretty samey and tedious. So maybe have a look at automating it. Automation does something else that’s pretty magical that I don’t think enough people talk about. Codifying a process also documents it in a formal way so that clicking the button isn’t the only thing another developer can do. Instead they can look at and understand the activities in the process and understand the mechanisms that produce the outcome

I will offer one caution about automation though. Ceremonies and practices are an important kind of activity that are healthy for team cultures. A code freeze and promotion might be a single click, but the milestone markes a change in phase in a team’s workflow and it’s important not to lose that sense of co-ordination

Top comments (0)