The Technical Practices
In Part I, we covered two non-technical practices that not only bring immediate and lasting improvements to software development effectiveness, but will also provide leverage to the teams who adopt the technical practices discussed in this part. If you’re not familiar with team rooms and retrospectives, you can find the first article in this series here. While these non-technical practices alone will help your team’s efficiency, neglecting to prioritize the effectiveness of technical software development practices will put your team at risk of struggling with several common problems, including:
- Code merges that take too long and result in missed milestones
- Core components of your company’s software system that are only well- understood by a few engineers on the team
- Lower maintainability of a code base that can cost a company tens or hundreds of millions of dollars on the long tail of a project’s value curve
All of these risks can be mitigated by using three core technical practices: Continuous Integration, Collective Code Ownership, and Test-Driven Development. These practices are found within the working agreements of almost every high performing development team – whether these teams call themselves “Agile” or not.
Many software development projects experience delays due to difficulties in merging contributions to a shared code base from multiple team members. Merging code contributions becomes more difficult the longer teams wait between integration events and the longer teams allow their developers to work in isolation. Continuous Integration, or CI, is a practice where each team member is making regular code contributions to the shared team code repository. These contributions are built and validated by an automated process, which provides a short feedback loop to the contributor as to the success of their integration of new code within the shared repository.
A key benefit of practicing CI is the ability to spot errors early and trace them back to where they were introduced. Small, frequent changes are much easier for a development team to manage compared to larger, less frequent ones. Practicing CI does have some prerequisites. First, an effective change management strategy must be chosen that meets the needs of the business and matches the scale of the organization. Many teams use a feature-branching strategy such as OneFlow to manage the change integrations. Templates and other tool support (such as Git extensions) provide teams adopting CI and GitFlow a leg up on their adoption, but come with the risk that team members may use the tooling without understanding the underlying concepts – your mileage may vary!
Second, teams must have an automated build process. Teams that have already made this investment are a good way down the path to adopting CI and will have to focus their efforts on the task of changing developer behaviors. A great tool for managers who want to encourage CI behaviors is GitPrime, a reporting tool that allows managers to ask and answer questions about their team’s performance using data produced during the development workflow. This tool will also help the manager identify developers who have sole (or near sole) ownership of a particular component in their software system, which is a risk one can manage using the next practice, Collective Code Ownership.
Collective Code Ownership
Martin Fowler defines three categories of Code Ownership: Strong, Weak, and Collective. Strong ownership is characterized by assigning an owner to each module or component in a system. Changes to a module or component are only allowed if they are performed by an owner. Weak ownership allows for changes to be made by developers other than the owner, but only with the owner’s review and approval. In both Strong and Weak ownership models, it is expected that a module/component owner will be individually responsible for the quality and suitability of any changes.
While the direct accountability arising from this approach is appealing to managers, the restrictions on collaboration and organizational scalability bring a high cost for those who practice these forms of code ownership. Development organizations that do not actively cross-train their employees on a codebase will inevitably be hamstrung by a few developers who become bottlenecks in the development process, or worse, be seriously damaged by the departure of a few key employees who understand critical parts of the codebase.
Collective Code Ownership eschews the notion of any individual ownership within a code base. The team is collectively responsible for all the modules and components that comprise the code. There will usually be team members who know more about one component than another, but every team member is expected to be capable of making changes and supporting changes made by their fellow team members. While some have said this can create an environment of “no code ownership,” we often find that other team dynamics determine the level of investment that teams display in owning their code bases. The clearest benefits to practicing collective code ownership are the elimination of personnel risk and the ability to scale development efforts by avoiding bottlenecks in code changes, which are further enhanced by Test-Driven Development.
Many software development teams are blindly told to “unit test all the things” or to achieve code test coverage targets by their managers. Teams that are directed by meeting coverage targets often skip refactoring their code altogether. Their focus becomes hitting a coverage metric, leading some to identify 100% code coverage as a tragedy. It’s quite unfortunate when these cases arise because some of the biggest gains to software development effectiveness are not borne out of the unit tests but out of refactoring the code. Teams that implement Test-Driven Development, however, are able to create a dynamic, reliable codebase that is created with quality in mind.
Test-Driven Development, or TDD, is quite simple to describe: write a failing test, write the production code that makes that fail test start passing, and then refactor the code you just wrote (ideally both the test code and the production code). This technical practice is one of the most difficult to see a team adopt and achieve full fluency. A developer will need to rethink his or her approach to problem-solving if they are to become facile with a test-first workflow. New tools such as unit testing frameworks and mocking frameworks will have to be mastered. Refactoring is a first-class technical practice unto itself – the canonical book covering this practice weighs in at nearly 500 pages on its own! These points are not made to dissuade adoption of TDD, but rather to set appropriate expectations amongst both developers and their managers.
While team productivity will decrease initially as these practices are learned and developer proficiency is gained, the payoffs in the mid and long term should not be underestimated. Some teams report achieving a “bug-free” project by keeping their code clean and smart application of automated unit testing. All teams that achieve a fluency with TDD will see great improvement in their software development effectiveness.
One of the best approaches to adopting these practices is to inspect the impact that each has on your team’s progress during a regularly occurring retrospective meeting. Effective application of these practices is highly dependent on the team’s ability to tailor the practices to their needs. As a well-known adage says, there are no silver bullets. Having a coach can help your team focus on why you do these practices and how to do them effectively. It can also help your managers adjust their metrics and practices to build a system that encourages continuous improvement. Companies that have such a system in place hold an advantage over their competitors. If your company is looking for a coach or other means to improve its development effectiveness, a Software Development Effectiveness program can help.
by Michael Stricklen, Executive Consultant