Balancing architectural goals with feature delivery
Nov 22, 2016
While working on small features, developers can sometimes — to their team’s detriment — misinterpret YAGNI (You Ain’t Gonna Need It) as “don’t waste time thinking about the big picture”. We think this can mean the difference between slogging through repetitive tasks or having the capacity to add awesome features that make the whole team — and the client — more productive. In this post, I’ll discuss the methods and tools TrikeApps has used to keep delivering features while nudging our codebases towards our architectural ideals.
Fowler Refactoring produces better outcomes than Heroic Cowboy Refactoring
We are big fans of Martin Fowler’s seminal work Refactoring. In this book, Fowler describes a technique of making very small, non-breaking changes to code and committing after each step. When working in this way, a developer can be interrupted at any point in the refactoring process and still have made problem-free progress towards their refactoring goal. This can be contrasted with what we refer to as “heroic cowboy refactoring” where a developer dives into the code, makes the change they wish to make and then goes on a caffeine-fuelled bender until 3am fixing everything that was broken (and usually missing something critical). Regardless of which approach you choose, they have an important common element - a clear goal of the refactoring effort. A goal may be to make it simple to implement a new feature by modifying some existing code, or just making a class easier to understand and maintain by breaking big blocks of code into smaller methods.
We apply Fowler’s refactoring technique not only to small feature implementation, but also to architectural changes. In the same way we apply Rename Method as a single non-breaking step in a class refactoring, we can make structural changes to the persistence layer of a web application while preserving the same API endpoints and user interface. This may be a single step towards the larger goal of changing the way an object is represented across a set of systems, but it is a step we can implement and deploy in isolation to the rest of the work.
Determination and communication of goals keeps the team moving in the right direction
Team leads must be able to step back from the coal face and recognise patterns in reported bugs or repetitive tasks to identify worthwhile architectural goals. They must be given the time to process this information and communicate it with their team.
TrikeApps has a monthly architecture review where the teams review the stated architectural goals of each system and check them for currency, relevance and progress. Doing this regularly means that these goals are front-of-mind for the team and allow them to continually move the codebase in the right direction. If your team has a good working knowledge of what architectural goals are currently in play, they can make stepwise progress towards those goals as part of a related story.
Mind-mapping tool, XMind, helps us visualise how a completed story can move the codebase towards a larger architectural goal. For example, our developers were having to spend a lot of time each sprint updating translations every time our client released new copy to their translators. Under the top-level goal of “allow clients to manage their own content”, we have a subcategory of “allow clients to manage their own copy” and then a reference to the specific user story that implements all or part of that functionality. Visualising story context this way also makes it easy to set sprint goals and demonstrate progress to the client.
Encouraging good software engineering practices makes architectural changes easier
Good software engineering discipline is enormously helpful when you come to making structural changes to your application. Adhering to the SOLID principles makes later modifications to code structure much simpler than dealing with dependency spaghetti. Code smell detectors like Reek are excellent at highlighting areas of your codebase that need some love and care, and repeated use of these tools can greatly increase your ability to anticipate a smell before you’ve even finished writing your code. We publish a smell count on our pull requests so the whole team can see whether the changes have increased or decreased the smelliness of the codebase. Test-driven development encourages good dependency injection practices, reducing the level of coupling in your code.
Are you making progress with architectural goals in your agile development team? We’d love to hear about your experiences..