Show all posts
1 month ago

Every feature considered harmful: Debts in software development

We tend to believe that every new feature is a good and valuable addition to a product. Quite often, it can be hard to not implement a feature, especially when you’re constantly hearing good ideas for new features every week.

The problem is that every new feature adds debt or creates a placeholder for future debt. You deliver more and more features, accumulating more and more debt. Most people don’t know how to work with debts. I don’t. Everyone knows about technical debt, but there are few more types of debt in software development:

  • Functional debt. The needs of users are changing, but the system forces them to follow old flows, and doesn’t cover new cases.
  • UI debt. Over time, the user interface can lose consistency and coherency, making the system harder to learn and understand
  • Quality debt. Bugs are ignored, the system shows unpredictable behavior, experiences errors, crashes
  • Technical debt. Architecture and code quality slows down development.

Any new feature in a product may become a source of all these kinds of debt.

DebtExample
Functional: Feature solves some problem. The problem’s definition may slightly change in the future, so feature functionality must adapt.All teams estimated work in hours, but then estimation in abstract points became popular. This new need demands changes in a product to support points as an estimation unit.
UI: A new feature quite often introduces new patterns and new UI elements.Designer decides to add new type of lookup field for some feature. This lookup is used in a single place and makes the whole UI less consistent.
Quality: A new feature almost inevitably adds new bugs. In complex systems, bugs may affect many other features.Live Update functionality affects many areas of a system. Bugs in unexpected places will follow.
Technical: Technical implementation of a new feature is almost always not ideal. It may cause existing system modifications that are not optimal, and side effects may be severe in the worst cases.Under time pressure, you implemented a quick solution for UI extensions via Mashups. It does provide great flexibility, but is very fragile. Any change in HTML or CSS can break some extensions. With time, many mashups are created and changes of this mechanism will be hard.

The image below shows several features in a product. Every feature adds some debts. In reality, it is extremely hard to add a feature with zero current and future debt.

features debts

Every Feature needs maintenance: functional, UI, quality, technical, documentation, localization. What if you have 100 features in a product? What about 1000? Can your software read emails already?

New Feature vs. Debt Payment

When should you add a new feature, and when should you pay back some debts? Let’s check the product lifecycle. Imagine you are running a very young startup. You don’t care about debts at this stage. You care about market expansion, new features, and growth. Now imagine the product is dying, it is too late to focus on debts, it’s over.

product lifecycle and debts

It seems the right time to focus on debts is when a product has proven its success on the market and is about to enter the “mature” phase. For a typical SaaS B2B product, this may be after 4-8 years. The chart above is valid for our SaaS product, Targetprocess. In my opinion, we started to focus on debts about 2-3 years late. We're finally coming over the hill, but it will take some more time to re-energize development.

Ideally, some part of your resources should be dedicated to debt payment at all times, but after 6-8 years it may even be a good idea to have a “Debt Return Year” or something. At this point, development speed decreases due to technical problems: UI consistency degrades, many features are outdated, bugs and performance issues annoy customers.

Moreover, you’ve accumulated enough customer feedback and now understand your domain much better. Mistakes you made and new opportunities became more clear. You need an ultimate clean-up to prepare a launching pad for a powerful push.

It is extremely hard to slow down and clean up the mess in a product. You are always afraid to lose market share and functional edge. I don’t want to be extreme here, just want you to think about every new feature you add from a debt perspective.

What to do?

There are several obvious consequences.

  • Build small systems. If you need a large system, build several small composable systems.
  • Less features -> less debts. Someone asks for a new feature? Default answer is “No”.
  • Modularization is desired, since you can re-write modules to pay back debts and reduce side-effects. This means you can have more features in a modular system with the same level of debt as in a non-modular system.
  • Remove rarely used features. When you remove a feature, you remove all related debts.

I want to finalize my post with a quote:

One thing that UNIX does not need is more features. It is successful in part because it has a small number of good ideas that work well together. Merely adding features does not make it easier for users to do things it just makes the manual thicker. The right solution in the right place is always more effective than haphazard hacking. — Rob Pike & Brian W. Kernighan