When is evolutionary design a good way to implement software?

Filed under: Design, TDD, — Tags: Big design up front, Emerging design, Evolutionary design, Just in time design, No design, Not enough design — Thomas Sundberg — Adrian Bolboaca — 2015-10-27

Designing software can be done in different ways. The time and effort spent in designing have an important effect on the result. An interesting question is when should you you allow the software to evolve using Test Driven Development, TDD, and when should you not allow it to grow using TDD?

Software Development Gang

This blog post is the result of a session at an open space with the Software Development Gang in Ghent, Belgium, June 2015.

Background

Before it is possible to discuss when to use and when not to use evolutionary design we need a definition.

Which domain are we talking about? This blog is about software development. Therefore, we are referring to software development and nothing else. That is, the process of writing a computer program that solves a problem its users have.

What do we mean with evolutionary design, then?

Big design upfront vs. evolutionary design

How do you know the difference between big design upfront and evolutionary design?

The line may seem, and is, fuzzy. There are no clear rules that can be applied that will tell you what type of design you are doing. There is no recipe that you can lean against.

First reaction

Thomas Sundbergs first reaction was that the question "When do you prefer to use and when do I prefer not to use evolutionary design?" is clearly wrong. Of course you would want to use evolutionary design. All the time. No exceptions.

It turns out, however, that it is not that easy. Things are seldom easy in the real world. There are definitely many cases when a test-driven approach to grow the design evolutionary doesn't apply. Many more cases than he could imagine.

Will not

These are examples of situations where we most likely would recommend using an evolutionary design.

Don't know the technology

When you are in learning mode and lack experience of the technology you are trying out. It is a throw away experiment. And you know that you will throw it away. Or reformat it to become a blog post or similar. Then you will probably not have a need to use TDD to drive the design.

The team knows it's impossible to do mistakes

When a team has the required experience of a domain and really knows that it is impossible to do mistakes.

We would still cover for regressions at some level, possibly end-to-end tests. Maybe not all of them as the domain and solution is very well known.

The framework can verify the wiring

We would not use low level TDD when a framework can help with verifying wiring of components.

We would, however, write end-to-end or acceptance tests. End-to-end would use the correct database etc.

Acceptance tests may or may not fake parts of the backend. The system can be accepted with a fake database, at least on a functional level.

The team doesn't have the knowledge to use tests to drive the design

An inexperienced team can't use tests to drive its design. Test-driving development is a skill that not every team possesses. If the team doesn't know how to do it, it might be a better idea not to impose this habit onto them.

TDD doesn't necessary lead to a good design. It can lead to good design, or at least prevent bad design. But learning good design is a skill in itself. Testing is another skill. It is possible to have both skills, but one of them will not automatically lead you to the other. In order to use TDD effectively you need to know the concepts of software design and constantly keep them in your mind.

We think it might be worth to point out that time alone does not necessary make you an experienced developer. It is possible to spend many years writing software and not gain a lot of knowledge or experience. Time and practice may make you experienced. Time alone doesn't necessary lead to anything useful.

Tests would break encapsulation of a supporting framework

There is an ongoing debate about test-induced damage to the design. We can't get rid of the feeling that this is an example of a design that could be improved and therefore allow testing.

An example is Rails and Active records that are supposed to be hard to test without breaking the encapsulation.

Maybe

These are examples of when we probably would use evolutionary design.

Want to learn a new framework or API

When you are in learning mode you could consider to use a test as a starting point for running small portions of the system. It would be very much the same thing as implementing a main method from where you can perform small experiments.

If you instead execute parts from a test, then you are able to keep all experiments. After a while, when you now enough, you can consolidate the knowledge and probably throw away the experiments.

Always

These are example where we would recommend to use an evolutionary design.

You know you will do mistakes of a specific type

It is hard to come up with good examples. That is mainly because we seldom trust our self not to do silly mistakes. We make silly mistakes far too often to be sure when we wouldn't make any.

Adrian Bolbocoa always uses an evolutionary design is when he need to write queries to aggregate data from multiple tables; he always write tests there because he does a lot of mistakes. The queries can be sql, in Hibernate or any other pseudo-language.

Thomas Sundberg always uses an evolutionary design is when he is working with regular expressions. His view is that if you have a problem and solves it using a regular expression, then you have two problems. Allowing the solution to grow using tests solves one of them.

You want to create a good design

You know the

This is the preferred mode for most development. This is why some developers call test-driven development, TDD, for test-driven design.

Conclusion

Evolutionary design is a great tool in many situations. But even a great tool should not be used for everything. Always remember, if the only tool you have is a hammer, all problems will look like nails. Many problems can be converted to nails. Some problems are, however, never a nail and will therefore require a different set of tooling.

It takes experience to be able to allow a design to evolve and avoid taking premature design decisions. Just saying "Listen to the tests" is not always enough for creating a good design. This is especially true if you don't know the difference between good and bad design.

A long experience as a developer doesn't always help. Many experienced developers still takes important design decision way too early in our opinion.

Evolutionary design is a learnable skill. A skill that requires training.

We think that

could be viewed as synonyms. They mean pretty much the same thing.

Acknowledgements

This blog post is written by Thomas Sundberg but he did not come up with all the examples himself. Adrian Bolbocoa is the co-creator of the initial thoughts for this blog post.

Resources



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
DevOps
Executable specification
Git
Gradle
Guice
J2EE
JUnit
Java
Javascript
Kubernetes
Linux
Load testing
Maven
Mockito
New developers
Pair programming
PicoContainer
Presentation
Programming
Public speaking
Quality
React
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS