Emergent Design in Software

Posted 3 years, 7 months ago | Originally written on 6 Oct 2020

It is easy to just get going in writing software which is designed for a narrow feature scope. Usually, we can even get away without employing any pen and paper. However, as the feature scope expands it gets harder and harder.

Under the waterfall approach the solution was to rigorously plan everything out before any line of code was written. The reason we perjoratively refer to waterfall as a by-gone method is testament to the fact that, while it sounded like the most rational thing to do, it was not very successful. Large feature scopes make for horrendously complex software with hundreds if not thousands of moving parts.

This is not only foolhardy as it almost always leads to unfunctional software but also tedious to those involved. There is little joy in being involved in a project that is ultimately going nowhere.

Emergent design turns things on its head and allows the right structure to evolve - it 'emerges' from the particular circumstances. The only necessary part of emergent design is a rigorous progression through the stages of development. As long as participants are constantly refactoring and integrating their changes you will be OK.

Emergent design allows you to write extremely powerful code because you are always building on a solid foundation. It's like building a skyscraper: it will only scale as high as its foundation allows it to.

So how do you know if you're being good at emergent design? It all falls down to the quality of your integration stage. The output of the integration stage should result in a dramatic reduction of logical tension in your code. Logical tension is the mental complexity required to understand code. It's the result of a poor mental model of the domain or current understanding. You know the feeling: you're hacking and hacking and hacking to get something to work. You've probably guessed several ways to use the API either because you don't have the time (don't we all) or the API is poorly documented. Finally you get it working - or just barely - then you decide to knock off and take a break. Unfortunately, you're unable to return to the code because of unforseen circumstances (you have another urgent bugfix that chews days out of your month, something happens in the real world, etc.) for a long time. By the time you get back to it you don't have the foggiest idea of what's going on. Everthing is all over the place. You've overwritten your bash history so you've forgotten the exact command you got it to run. Basically, you're upset at your past-self for doing such a lousy job. It's going to take several hours before you figure our how to get it working again. This is logical tension.

The integration stage attempts to answer the question: given the hack and refactor how has our mental model changed? What opportunities exist to reduce logical tension dramatically?

Another related aspect of emergent design is that there is a part of the development ecosystem that exists in the minds of your developers. They are the ones who have kept their face close to the cutting edge and have a very intricate understanding. You know how this works when you can immediately figure out the right fix from a seemingly cryptic error message. Therefore, your success at emergent design is only as good as the longevity of your team. Replace the team and there's no knowing where the design will head off to. By having a high turnover in a software team (or any team, for that matter) you severely impede your ability to grow. Of course, if you're not practicing emergent design then it is in your interest to constantly purge your team with the off chance that someone will turn up who can steer the codebase in the right direction. But if you are doing emergent design your code base can only improve with time and the time-to-feature* can be reduced to be as small as possible.

Benefits of Emergent Design

  • Scalable code base
  • Only solves current needs instead of wasting time on unnecessary features
  • Evolves towards the right design
  • Allows all skill levels to participate - with a clarifying design newbies can benefit from the integrated nature of the codebase
  • Low time-to-feature
  • Leads to harmony between the codebase and the language

Challenges of Emergent Design

  • Requires very strict discipline to execute correctly
  • Requires strong collaboration
  • Requires visionary leadership who will want to create as pure a codebase as possible
  • Requires participants to steer away from vanity code**

*Time-to-feature - is the expected amount of time required to add a new feature regardless of how complex it is

**Vanity code - code that is exhibitionary in nature and accomplishes (often fails to) what it requires superfluously