Technical debt and how to avoid it

Technical debt is when we use shortcuts during development, that are quick and easy, instead of opting for a more robust and generic solution. We promise to fix this later, i.e., paying the debt, yet forget about it. This results in growing list of TODOs/refactorings, a debt ridden code and an overall reduction in confidence on the platform.

Why you accumulate technical debt

The prime cause of accumulating tech debt is usually short delivery deadlines and changing business requirements. In order to ship faster, developers usually employ quick fixes and bespoke logic that become hard to maintain in the long run. Another popular cause is lack of senior engineering talent, that results in overlooking common pitfalls and long term maintainability.

How to recognize technical debt

If you see any of the following symptoms on your platform, it is highly likely that you suffer from technical debt.

  • Frequent alerts / firefighting in production
  • No alerting mechanism / alerted by users about the downtime
  • Infrequent / buggy releases
  • Regression while rolling out updates / features
  • Slow pace of development
  • Low developer confidence to update a working piece of code

Avoiding technical debt

There are many techniques you can employ to avoid accumulating technical debt and build a well functioning platform. Here are some of them that I tend to employ,

Design patterns

Modularize your code, discuss and agree upoon following popular design patterns. Reduce code duplication, ensure separation of concerns, keep functions small and modules abstract.

Testing

Testing is one of the most important part of improving confidence in the system and reducing technical debt. Write automated tests for every single line of code as you develop. Do not keep unit tests as a separate task to do, instead they need to be part of development. Add functional tests and integration tests to your platform to check various flows and ensure these run as part of your continuous-integration processes.

You can also use pre-commit hooks, to ensure that the tests are run before the commit is even applied.

Use tools

There are a ton of tools for every language to format, analyze and report issues with your code. Employ them, they are free and will save you a huge amount of time and effort.

Here are some of them that I use,

  • Java
  • Python
    • black to format your code
    • flake8 to enforce style guide
    • coverage to generate reports on code coverage
  • Terraform
    • Terraform has a built in formatter that can be run as terraform fmt -recursive

You can self host sonarqube which can read reports generated by jacoco/coverage and present it using a friendly UI.

Some of these can also be configured directly on the IDE so that there are warning shown while writing code.

Code reviews

During code reviews, ensure that there is abstraction between modules, functions are generic/reusable, follows agreed upon design patterns and testing is in place for the new piece of code added. Employ automated checks, supported by most version control systems, to check for these and reduce the burden on the code reviewer.

Set up rules such that code cannot be merged without approving reviews and without resolving all discussions/comments. Coach code reviewers on what to look at during code reviews and ensure there is a sense of accountability and ownership across the team, not the the lead or the manager.

Miscellaneous

A few other techniques that you can use,

  • Do frequent integration and deployment
  • Plan budget to improve existing code base in every sprint
  • Keep dependencies upto date and never use an older version of a library unless there is an unresolvable compatibility issue