This post has been migrated to http://www.thinkcode.se/blog/2015/10/27/when-is-evolutionary-design-a-good-way-to-implement-software
Designing software can be done in different ways. The time and effort spent on designing have an important effect on the result. An interesting question is: When should you allow the software to evolve using Test Driven Development, TDD, and when should you not?
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.
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?
We see it as the process of building a solution in small steps and allowing concrete needs to steer the development. Needs that show up when we implement the solution. Needs that we didn’t really think of before we started the development.
That may seem as a strange view. Isn’t all development done this way? It turns out that the answer is no. All software isn’t implemented this way.
What is the preferred way to develop software then?
As we see it, designing when needed and building in small steps between the design sessions. Allow the design to appear when there is a need. Test-driving the implementation using Test Driven Development, TDD. Some initial design is OK and usually a good idea. You don’t have to start with no design. You probably have an idea of where you are headed.
This is the opposite of big design upfront where all details must be specified before the development is allowed to start. Big design upfront is sometimes the result of an offer for fixed price projects. If you don’t do a lot of thinking and estimating upfront, it is very complicated to give an offer that is valid. An offer you won’t loose money from but rather make money from. In our experience at least, big design upfront is never a good idea.
Remember, it is in doing the job you really discover what you need to do. Things that you missed even when you though very long and very hard.
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.
We think that when we are designing for days, it is a clear sign of something big. It is an example of a too big design upfront.
We prefer to design for a few minutes up to an hour at a time when there is a need. We never want to design our solution much more than an hour.
Taking small steps will allow the design to grow and evolve as needed.
Thomas Sundbergs first reaction was that the question “When should you prefer to use evolutionary design and when should you 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.
These are examples of situations where we most likely would not 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 on some level, possibly end-to-end tests.
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 necessarily 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.
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 know enough, you can consolidate the knowledge and probably throw away the experiments.
These are examples 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 ourselves 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 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 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
- Supporting frameworks
- Test Driven Development, TDD
- You don’t know exactly what the solution should be, at least not every detail
This is the preferred mode for most development. This is why some developers call TDD test-driven design.
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 take important design decision way too early in our opinion.
Evolutionary design is a learnable skill. A skill that requires training.
We think that
- Evolutionary design
- Emerging design
- Incremental design
- Just in time design
could be viewed as synonyms. They mean pretty much the same thing.