While coding in XP ways for the past few years, I have realized that in any Agile project which crosses the one year mark, there is a definite chance that the code base is not as flexible as it was during the start of the project. As releases happen, more and more stories are churned out by the developers. This accounts for a lot of additional code - a lot !
As the application evolves, so does its architecture. It becomes so important to look for ways to enhance the existing design rather creating one for every new feature or advancement. Patterns emerge and are handled to ensure maximum reusability and code quality.
But not without challenges! Over a period of time the reasons behind key decisions may be forgotten. Critical tech debts may be uncleared. Lack of context and ambiguity may slow down the pace of development.
I have outlined four simple and practical methods that will make the adoption of incremental design for a project that much easier. What’s more is that three pertinent target groups - the individual developer, the team lead and the team at large can use these four methods, just as effectively as the other.
It is of paramount importance that team members understand the business requirement.
One should ideally avoid creating 'execution flows' for each use case. The goal of good design is to produce maximum code reusability and simplicity. Trying to identify the minimal set of responsibilities for each component or layer and creating abstractions, accordingly, is a recommended best practice.
One could ask themselves, this question - what is the least that this component should accomplish? For any thing else, should we create a separate component or unit of responsibility? The answer can help identify the common/reusable logic. This practice also prevents big, bloated components that have virtually zero reusable code, within them.
I have seen strict TDD be compromised, many times. A test that’s written after all the code is already written, reduces chances of it being effective. Why does this happen in the first place - Sometimes because of release cycle pressures or sometimes to prevent clean test helpers and builders from getting too complex or because of a poor safety net that exists within the code base leading to disinterest.
Though none of these scenarios are reasons enough to stop a developer from practicing strict TDD, it’s at these points that stepping up can make a significant difference. The team should not lose hope and boost their morale against such ‘forces’. This is because incremental design may never happen without strict TDD.
The design of the application evolves all the time. During code pairing, it is the role of the 'navigator' to look at the larger picture. There are instances where I have seen both the paired up developers taking on the role of the 'driver' i.e only looking at ways to solve the problem at hand. And that’s not the quality of collaboration, recommended.
Another best practice for project members is to take major changes in design or architecture to a 'white board' discussion. This brings in collective thoughts and feedback. Also important is refactoring becoming a regular practice of each story. Else, time would be expended with frequent re-design and this is not refactoring!
Developers could have frequent dev-huddles. This will enable the team to put up related stories to play upfront, discuss possible design approaches - all this helps keeping abreast of what is to be expected in the days ahead. Also, tech-debt is something that the entire team should be monitoring and it helps to 'pay' them off!
The team could carry out an exclusive tech retrospective, after every release to have a clear overview of the architecture’s evolution, the kind of tools that helped them and the practices that have been effective, etc.
Identifying future spikes that may be necessary both, from a tech-debt and future requirements point of view alongside the entire team’s feedback is valuable when shaping the course of the project.
While building and maintaining enterprise level applications is a whole other ball game with a different set of challenges, keeping the bigger picture in mind makes every challenge surmountable.
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.