Evolutionary requirements, incremental design, refactoring and rework


Yesterday, I had quite a lively discussion with a colleague of mine. He wants to do Scrum. He wants to do Scrum but still wants to spent some weeks on upfront analysis and design. He does not believe in evolutionary requirements and design. One of the reasons for defending that position is that he considers that incremental means a lot rework. He also assimilates refactoring and rework. Consequently, he deduces that being evolutionary means reworking existing code each time we would implement a new feature.

As you might have guessed, I argued the opposite. I’ll try and explain why in this post.

First, let’s start off  by defining refactoring, rework and rewrite.

  • Refactoring: Change the code to improve it in terms of readability, maintainability, cleanness, reuse, etc while preserving functionality. If there are bugs, they remain in the code. When you refactor, you don’t change functionality.
  • Rework: Change the code to change its behaviour, for example to fix bugs or improve performance and stability. Rework entails changing the functionality. You did the wrong thing and want to do the right thing.
  • Rewrite: Scratch something and start afresh. You obviously did the wrong thing and did it wrong.

In Agile, you build the system iteratively and incrementally. You only write code for the scope of your iteration, no more, no less. This means that the functionality will change for sure. That’s not a problem because, thanks to the rapid feedback, you usually will seldom need to completely change too much completely.

By doing incremental design, you:

  • Code only to implement what’s in the scope of the sprint.
  • Design only to fulfill the needs of the scope of the sprint.

Plan stories earlier if you need to minimise risk and generalise some design early.

  • Triangulate to generalise behaviour instead of thinking ahead a so-called generalised behaviour and that will not be used after a while and possibly never.

By doing so, you eliminate waste:

  • By coding only for the functionality at hand, you don’t overdesign. The code you write is tested and accepted by the end of the sprint. If you wrote code for future needs, it would not be accepted because the functionality would not be tested
  • You don’t code anything based on speculations. The code you write is used and exercised immediately.

Iteration after iteration, you write new code and change existing code. Usually, you do the following:

  • Refactor continuously so as to keep you code base clean and don’t incur technical debt.
  • Rework periodically to fix bugs and misunderstandings. This usually happens during the sprint and concerns little portions of code. If defects made their way to production, the code will be fixed during a future sprint.
  • Rewrite seldom, except if you incurred a lot of debt or that something was done completely wrong.

The continuous refactoring allows for keeping the code clean and easier to evolve. Such evolutions usually take the form of new code driven by tests. Once the tests pass, refactor the code to avoid duplication and keep it clean.

In conclusion, you refactor a lot, rework a little and rewrite as less as possible.

Some interesting links on the topic:

%d bloggers like this: