Debate, Technical Debt

I remember my last interview, they ask me:

If you are approaching the project deadline and you haven’t finished all the work, would you sacrifize quality in order to finish or would you either sacrifize some of the user stories towards maintaining quality?

Of course my answer was maintaining quality, quality is my Motto (read this article by Richard Vidis). Technical Debt is widely regarded as a bad thing; that should be avoided or should be paid back as soon as possible. Everybody wants to do things right, isn’t it?

Lately I listen a podcast in Software Engineering radio about Technical Debt and that was a mind blowing for me:

Is it possible that Technical Debt could be not such a bad thing?

Today I wanted to debate with you on:

  • Comparing Technical Debt with Financial Debt and see why Technical Debt could be a good thing.

  • Finding Technical Debt in our code.

  • Ways of paying back Technical Debt in our projects (considerations to be made in order to decide if we should better repay, convert debt or just pay the interest).

But first things first…

What is Technical Debt?

What decissions have I made in the past that prevent me from delivering features today?

Developers have the choice to implement new features in two different ways: one is to do it quickly and messily, which will make future changes very hard. The other is a clean and smart solution, which takes longer to implement, but makes changes easier in the future (Martin Fowler).

But why should the sponsors of a project accept higher costs for a clean implementation of a feature if the same feature, implemented with a messy solution, delivers the same functionality and costs less? Why should they spend money on automated test coverage? Tests are not features and therefore don’t deliver business value!

Although messy code or code without tests works perfectly for customers if it delivers the desired business value, this will lead to an uncontrollable code base, extremely specialised developers and eventually to an inflexible software product. A sufficient amount of messy code may bring a whole engineering department to a stand-still.

Similarities and differences to Financial Debt

Code with low quality and no automatic test coverage can be compared with financial debt. This code is like a financial loan, which imposes on all stakeholders (not only on the developers) a debt incurring interest for the future.

Similarities

Differences

Strategic design of Technical Debt

Strategic Design says that:

A system can’t have the same high level of quality throughout the system.

We can choose to:

  • Leave to chance which parts of the system have a good or a bad quality or

  • To actively control the quality. Messy code, which is rarely read or touched and doesn’t implement important requirements, does not have to be absolutely perfect and therefore we don’t need to spend a lot of effort on refactoring it into great code. So the question is which parts of the code should have high quality?

When do you think we should incur in Technical Debt?

Understanding Strategic Design and Technical Debt leads to better communication within a project and therefore to better decisions from the stakeholders of a software project.

Which are the stakeholders of Technical Debt:

Identifying Technical Debt

A major problem of Technical Debt is that it’s not obvious. Anyone can see the amount of debt on an account statement. But how can a team actually recognize Technical Debt? What are the indicators?

How to manage Technical Debt

We have seen that Technical Debt cannot be ignored. Even the non-technical management on the client side must have an interest in managing Technical Debt as smartly as possible to achieve the best balance between short-, mid- and long-term success. What can a team do to avoid wasting its time with unimportant beautification but still make meaningful business decisions for improving their code quality?

Buffer-task

The team creates one buffer-task per release with e.g. Engineering day. Team members can add that task for not yet scheduled refactorings into the buffer. So it is used for yet unknown problems that might appear in the future. Such a buffer is very easy to schedule and use.

However, it also poses the risk that time is wasted on unimportant work. A buffer doesn’t force anyone to consider whether the time is spent for useful refactorings or not. Most likely the time of the buffer may not be optimally utilized - and especially deciding which refactorings will be done, although that should really be a business decision. Using buffer-tasks unfortunately means that what should be done is not really defined.

Cleanup-releases

Some teams do a purely technical release to improve the codebase from time to time. This approach is only useful if a list with the really necessary refactorings already exists. Otherwise the team risks wasting time on unimportant refactorings. This approach must also be supported by the business side because it might delay new features. Of course this requires that business people understand Technical Debt. You should think about a pure technical release to clean up the codebase and to rework the architecture if some major effort is needed. For example the same parts of the code might always cause problems during development or operations, the current architecture might no longer fit the current requirements. Such problems cannot be solved within small refactorings. A cleanup release allows extensive changes.

It makes no sense to do a cleanup-release after a very hectic and time-critical release that has created a lot of Technical Debt. There is only little experience with the new code base, so no one can say which part of the code needs to be improved. The danger in changing code which does not really need improvement.

Technical Backlog

The technical backlog is an established best practice to define purely technical work packages. Tasks for this purpose are created in a task tracker or requirements management tool. Each task has a brief description of the technical change to be made, why this technical change is important for the project and in which part of the code the technical change has to be performed. As with any other task, we need an estimate of how long it takes to develop a good enough solution. In addition we need to estimate the interest which is inherent in this code. A precise estimation is difficult, but often a rough estimate such as ‘small’, ‘medium’ or ‘high’ is sufficient to guide a decision. Ultimately we also need a probability: how likely will this code be read or changed in the not distant future?

Big advantage: Prioritize

Strategic Design has taught us that not every part of the codebase must be very good but only those parts whose changeability brings business value or need to be changed frequently for other reasons. So concerning Technical Debt code quality of rare or never changed locations can be ignored. Also important: Code should never be refactored without a good reason. So if a new requirement provides concrete business value then we should never implement this requirement based on badly written code but refactor beforehand. That way the really important requirements can be implemented to a “good enough” standard. When the team has been assigned to implement such an important feature in an upcoming release, the refactoring should be budgeted and then implemented.

This procedure ensures that no unnecessary beautification work is done and that refactorings are always directly associated with a requirement. This allows the customer to discuss and prioritize the refactoring with the team. The customer can also decide whether or not a requirement should be implemented based on debt and interest. There might be other factors to take into account such as time-to-market. The customer might also already know about future changes to the code which are based on these requirements. He might therefore decide to refactor to a clean code base in order to save costs in the future.

Hope these concerns were interesting for you.

Written on April 21, 2015