Agile has become one of the most beneficial and needed change in software development. It has helped to move the focus of software development:
- From: Project managers, architects, documentation and requirements.
- To: Programmers, implementation and opened discussions.
Paradoxically, some companies and programmers are pushing this change of paradigm so hard, that they are falling in the very same errors that doomed waterfall: Lack of flexibility and dogmatism.
One of the most popular dogmas among the agile community is: “you shall never do any design.”. This dogma it’s been mostly influenced by TDD, and that’s why this article focuses on it
TDD and its core pattern “red-green-refactor”, is an emergent design engineering practice that can dramatically improve the quality of the code. Being based in emergent design means that is not necessary to do any explicit design in software development, but that the design will flourish from the implementation.
The issue with TDD, as with any other emergent design approach, is that it doesn’t make any differentiation between the implementation design level (tactics), and the architectural design level (strategy). This lack of differentiation between strategy and tactics, for simple developments is OK, but for more complex developments becomes an issue.
A good metaphor would be climbing a mountain. Let’s imagine two different approaches; one where every single step of the route is prepared beforehand, and the other where we would just start climbing paying attention just to what we have a few steps ahead of us. The first approach would be the equivalent of an extremist waterfall approach; the second would be the equivalent of an extremist approach to agile. Obviously none of them would work.
Software development is, in that sense, like climbing a mountain. It is necessary to have a strategy and a tactic. Is also important to realize that, as climbing, we have to adapt to the different circumstances we face in the day to day of software development.
TDD, as any other emergent design technique, is ideal for the tactical part of software development, but they ignore the strategic part, which for complex developments is very important.
One of the biggest factors to determine whether a development is simple or complex is uncertainty. Having a high uncertainty means that there are a lot of critical details from the development that are no evident. When uncertainty is high, is when we can take advantage of doing design.
One sample of simple development, with almost no uncertainty, and which won’t require any previous design would be developing a component that would take a quantity and a percentage and would tell us what the interests are at the end of the year.
An example of a more complex development would be to develop a component that would be able to resolve a Sudoku. In this case we would take advantage of doing some previous design.
When doing designs, is important to take into consideration the following guidelines:
- The design should be informal. It shouldn’t have to be in any particular format or using any particular methodology.
- The design should be incomplete. We should distance from the Waterfall BDUF, we only want to capture the 20% of the essence, which is going to help us fix the 80% of our problems.
- The design should be small, and should take short time. We shouldn’t expend more than 2 hours doing the design, if so, the reason would probably be that we need to split the development into smaller development items.
- The design should be democratic and simple. This means that there shouldn’t be any architect throwing the design to the programmers. The design should be done via open discussions with your colleagues, and simplicity should be preferred to any other approach.
- The design changes and evolves. We have to move away from the waterfall approach where the design is static, in agile, the design should be used to help us identify the bigger picture and it changes as we discover hidden aspects of what we are developing.
Continuing with the previous Sudoku example and with the design guidelines just commented above, one design approach for a component that resolves Sudokus summarized in a few points, could be:
- To find the solution for a Sudoku we have to guess, one at a time, what are the missing numbers in the empty cells.
- The best approach to pick the next number to guess is to find a blank spot in the Sudoku that can only hold 1 valid number.
- When a solution for one cell is found, a new Sudoku with that cell filled is created and recursively we keep looking for the next solution until all the cells are filled. Then the Sudoku is solved.
- When there isn’t any cell with an unique possible value, we have to search the cell with the less amount of valid possible values and create an hypothesis from there. That’s it, from the list of possible values, pick randomly one of them.
- With the hypothesis we keep looking for the solution as if it wasn’t an hypothesis, but a completely valid solution. The difference is that with hypothesis, if we ever get to a point where there isn’t any possible solution, we go back to the originating Sudoku of the current hypothesis, and we create a new hypothesis.
- The hypothesis can contain other hypothesis.
It is important to realize from the previous design that it doesn’t get into implementation details, and is very informal. They are only a few guidelines to help with the development.
Even thought, they may not seen very important, this mini design sessions are key to help developers to obtain the bigger picture of what they are developing. Remember:
- It is not right to try to guess beforehand what are all the directions we have to take paying no attention to the road.
- Neither it is, to start walking only to realize 2 kilometres later that we took the wrong direction.
Balancing out the right amount and timing of designing and coding is, with any question, a quality common to all the best programmers in the world.