Radically Honest Blog

Software Products Degradation [1/100]

I’m writing 100 posts about product development and product management. Three years ago I already had a similar experiment when I wrote every workday and indeed wrote 100 posts (in Russian, sorry). For this challenge I take weekly cadence, so you can expect a new post every week (or less). This is the first one. Let’s go.

Today I want to talk about a fundamental law that destroys all software products — entropy. Many external and internal things affect the products and lead to degradation, decay, and death 🪦. Most products die because of entropy (eventually).

Entropy is not intuitive. Let’s try to check what destroys software products first.

1. “More features” race 🐸

New features inevitably increase entropy. The product becomes complicated, more things can go wrong and decay. The rule is simple: more features — higher entropy. Every feature has to be maintained, improved, connected with other features, and polished.

Relentless and fast new features addition over the whole product life cycle is a direct path to the products graveyard. When you are focusing on new features only, old features deteriorate and not-so-old features have no chance to become great. Everything is mediocre at best. Product is in permanent stress, it tries to be on the edge, but it will be always unbalanced.

entropy

At some point, successful products have to stop focusing on new features and find the right balance between new development, polishing, and maintenance. This is more art than science, but great product managers (or founders) are doing it. Let’s take Notion. They added so many features in the first few years, but then stopped for more than a year and re-focused on polishing and re-architecture things. Or take Linear. Their approach to product development is very balanced, new features are very well designed, and attention to details is great.

I tend to rush with new features, but I do hope to find the balance eventually since at least now I know my problem :)

2. Technical debt 💩

Almost any product accumulates bad technical decisions, strange patterns, eternal “temporary solutions” and workarounds. System architecture loses its style and grace under the pressure of the functional requirements. Key developers leave the company. System knowledge fades. Product degradation accelerates. Eventually, the system becomes so complicated that a trivial fix (typo) can take an hour. And any serious feature can easily take 6 months to implement and release.

Programmers have tools to fix all this mess (in most cases). They can refactor, decompose, rewrite, clean up and fix. Unfortunately, the “more features” 🐸 movement inside an organization always conflicts with technical quality. Sometimes the reason is right and clear (win the market niche). But sometimes it is not (we need more sales). To be honest, this is hard to resist. As a product manager, you live under constant pressure from the competitors, your vision, and your customers. You tend to prioritize functional requirements on top. You want to win the race! Eventually, software development speed becomes too slow to grow. Entropy takes its toll 🪦.

3. External context: Technological progress

Processes and technologies are evolving. It might be not very visible in a few years timeframe, but in 10 years progress becomes visible. New languages, new platforms, new libs, new approaches. You can build a fairly complex web app in a few hours today, few decades ago you had to program in C (CGI FTW!).

Technological progress changes the external environment and helps new products to outpace old products. New technologies may increase development speed and help new companies fight entropy better (more details below).

Old companies try to innovate to survive, but results are mixed. The evolutional approach to existing products has its limits. It is extremely hard to change technologies gradually and not die. Essentially, you have to start from scratch, but this is almost as risky as the new startup, so the fail rate will be around 80% I think.

4. External context: Market changes

Markets do change. New niches appear. Old niches die. User requirements change and features have to be changed. Unfit products with high entropy can’t survive fast context changes 🪦. Young or lean products with low technical debt and not so many features can adapt 🧬.

How to fight product entropy?

The general rule is to stay fit and agile as long as possible 🤺.

  1. Software architecture should increase system agility to embrace external context changes.
  2. Product development process should increase product’s fit and agility, balancing between new features and technical debt, and enabling new explorations.

Software architecture practices

1. Balanced module architecture 🗺

Isolation of compartments by bulkheads works. Communication protocols work. Right modular granularity works. However, these practices are not so easy to apply right. For example, microservices are trendy but might be too granular to keep the system agile. Microservices are complicated. On the other side of the spectrum, we have monolith architecture that is hard to change.

I personally like a balanced approach, where you have a relatively heavy core service, and many smaller satellites services. What should be in the core? Hard to say, it depends on the system.

A continent with islands is a better model than 100 islands.

Long time ago you could drive to Australia from New York. But now platypuses live there!
Long time ago you could drive to Australia from New York. But now platypuses live there!

2. Higher abstraction level 🦒

Generalization usually increases agility. Here is the trivial example. Let’s say, you want to show a list of entities in 5 different places in a product. Most likely a single generalized List component will be better than 5 different list components. In the ideal world, this List is a browser component that you just use and don’t reinvent the wheel. But it’s just a dream, even the universal rich-edit field is not there (what a shame).

Higher abstraction level systems have better chances to survive and explore new niches, but they are not free. In general, they are significantly harder to build and they take more time to build. So you have to balance again and raise the abstraction level for critical parts only.

Product development process practices

1. Balance new features and maintenance

In the first several years of the product lifecycle, new features are much more important. “Several years” is not very helpful and we need a better indicator of when to switch. My personal preference is: you have the product/market fit and a good market share. For example, if you fight for the niche in a team productivity space, $1-2M ARR might be a good indicator. When you hit these numbers, you may want to put existing features improvements higher.

Balance is never easy since blind features race is very appealing 🐸. Anyway, some balance should always be there, 10-20% of development time should always be dedicated to architectural changes and fitness.

2. Spikes and re-writes

Re-writes are risky, but sometimes rewarding. An engineering team accumulates knowledge and may find a radical approach to simplify the architecture. To check this project viability, you can do a spike (short and focused activity to build a walking skeleton and try a new approach). Spikes reduce risk and help to make the right decisions.

Anyway, re-writes are almost inevitable. Sometimes they are desperate attempts to forget about technical debt and start from scratch. Sometimes they are just crazily optimistic attempts for no real purpose 🪦. Controlled re-write attempts is a better way to balance new possibilities and missing chances. Note that re-writes and modular design are friends. It is much easier to re-write a single module than the whole monolith.

3. Technical debt management

To be honest, I’m quite skeptical here. For any product manager, it is really hard to make a deliberate decision to include tech debt cleanup into the process. Most likely it will be a sporadic non-systematic activity that will be halted by new requirements and new features.

I believe in re-writes more and I think punctuated equilibrium works better here than continuous improvements. Critical modules should be maintained and improved, all other modules can be re-written later.

Moreover, with this approach, it’s easier to have some agreement between product and engineering teams. For a product manager, it’s easier to dedicate permanent maintenance time for 3-5 modules than for 20+.

TLDR:

  • 🤷‍♀️ More features → higher entropy
  • 🦖 Unfit products with high entropy can’t survive fast context changes
  • 🤺 Stay fit and agile as long as possible (as a product)
  • 🌎 Architecture: A continent with islands is a better model than 100 islands
  • 🦒 Architecture: Raise the abstraction level for critical parts only
  • 🪤 Process: 10-20% of development time should always be dedicated to architectural changes and fitness
  • 🔮 Process: Critical modules should be maintained and improved, all other modules can be re-written later

Entropy is hard. To prevent product degradation you have to spend serious effort. Red Queen was right.

Psst... Wanna try Fibery? 👀

Infinitely flexible work & knowledge hub.