Quality is one of those things that every team has trouble with, let alone Agile teams. There is never enough time for testing, there are always too many bugs to be fixed, and testing everything just costs to much to do all the time. I guess quality is just out of the reach of most teams…
But it doesn’t have to be that way!
Instead of fixing bugs after they’ve happened, why not learn to prevent them? Its not hard. All it takes is dedication, a lack of tolerance for defects, and continual attention to quality. But here’s the sneaky little secret. Agile teams should be doing each of those things already.
Agile teams that are paying attention to the Agile principle of Continuous attention to technical excellence and good design enhances agility should already consider defects to be dastardly devices to deflect their development. And to prevent them, they should have strong quality processes in place across the team designed to create quality into their systems as they’re being designed, defined, development, and deployed.
The rest of this post will look at how Agile teams can create processes and feedback cycles intended to prevent defects, rather than spend lots of energy finding and fixing them after the code is written.
Starting from the Basics
There is no one single thing that Agile teams can do to get to this holy grail of defect prevention. There are , however, layers of things that they should do, each representing another piece of the larger quality process. The big thing that each of the layers has in common is the idea of generating feedback on the deliverable on the layer immediately beneath it.
At the ground level, is quality code, everything above it feeds into creating that quality through feedback and communication.
Perhaps the most controversial Extreme Programming practice, but also potentially the most useful in terms of creating quality. Pair programming really is two programmers sharing one computer, both working on the same problem, eyes on the screen, hands sharing the keyboard and mouse. It is intense, it is exhausting, and it is exhilarating. From a quality point of view, it represents two people watching as a piece of the system evolves, catching code errors, design errors, or requirements errors as they are being created. This is the smallest, tightest feedback loop in the system, informed by all the layers about it. At its essence, Pair Programming will catch quality issues at the keyboard.
Test Driven Development and Refactoring
The standard way code should be developed on an Extreme Programming team, and any team for that matter, is to use tests as a probe into the code and design being created or changed. These tests force the pair to think about the structure of the system as they build it, make them decide what the code they are about to write should do, and what kinds of side effects will be observable once the code is written. The discussions that lead to a test being written are deep, completely submerged in the knowledge of the business and code details of the system being built.
The workflow is pretty simple. When a pair starts to work, they leverage the existing tests to verify the quality of the system. Assuming all the existing tests pass, they can move forward in creating something new. If any of the existing tests fail, then something is amiss, and it would be foolish to add more code onto a potentially broken system. (This shouldn’t happen often on a mature team, but mature teams always check to be sure!)
Once they get to a working system, the write one test as that probe into the design of the system, then implement that test until it passes. At that point, they can run all the tests, as they did above, to make sure that their changes didn’t break something else. In effect, they are able to retest the entire system after changing any line of code! Defects have nowhere to hide.
Before they continue on to the next test, they move on to the next phase of this workflow, refactoring. This is the process of making sure that the system is still easily changeable after the modification they just made. They go in and explicitly remove duplication, make individual lines of code easier to read, and generally leave the area better than how they found it. This preserves the ability of the system to be changed by future tests. The enabling tool that lets them improve the code with impunity are the safety net provided by all the tests they’ve written – these tests are run multiple times during this cycle to make sure nothing broke. Without these tests, designs slowly rot from neglect.
Working through small tests helps pairs focus on solving lots of small problems that add up to larger implementations. This lets them finish small bits of functionality frequently and check those completed bits into source control multiple times per day. That’s right, you heard me – checkins from every pair multiple times per day! With all that code churn, with all those changes flying around the system, we need a way to make sure that nothing broke. That something is called Continuous Integration, or CI for short.
CI is a process that ensures a system works 100% after each and every change. A good CI process starts from a clean slate, checks out all of the code from source control, compiles the code (if needed), runs all tests, and shouts really loudly at a team if anything broke. In theory this should very rarely happen, since people are running these tests all the time on their local machines. Sometimes accidents to happen, though, like forgetting to check in a file or having code work on a single box because of some fluke of configuration. CI is the “canary in a coal mine” to tell you something is amiss in the code. When the build is broken, teams should drop what they’re doing and fix it.
Acceptance Test Driven Development
Now we’re moving a bit out of the technical area and into that nebulous region between the developers and the Product Owner. Acceptance Test Driven Development, or ATDD for short, is a key part of this transitional region. At its most basic, the tests in ATDD are representations of the business process being implemented. They are executable, written in the language of the business, and are a product of very close collaboration between the Product Owner and the developers to define what the business process is, at a very fine grained level.
This collaboration takes some time and several conversations for each user story being implemented. Over time, a level of shared understanding about what to build and how it should work is developed across the entire team and captured in the ATDD tests. The developers can now use those tests to make sure they’re building the right thing.
From the Product Owner’s point of view, these tests are an accurate, shared understanding of how a feature works from a strictly non-technical point of view. It defines the workflow, some sample inputs, and the expected outputs from that data.
And the developers can use that to understand what to build and to verify that they have built it. And once they’re finished, once they’ve gone through the process of checking in their code, these ATDD tests can also run as part of the CI build. Now, not only are those builds verifying that the code works correctly, but they are also verifying that the individual business functions of the system work correctly. This gives two levels of “correct”, increasing our chances of Continuous Quality.
Ron Jeffries is fond of saying that the only metric that matters is the number of “running, tested features” a team has. There are others who have taken that one step further and talked about needing to have those features in the hands of real users to generate usable feedback. This requires teams to be able to create frequent, incremental releases of their software, and to be able to push those out to some kind of environment outside of their team room. I understand that some large commercial entities have processes that have to be followed to put something out into public view, but even if you can put it out to where internal users can play with your system you’re able to get some kind of feedback. Closer to the customer is better, but some feedback is better than none!
So, because we know we have a quality system, due to the previous 4 layers, we’re able to take almost any successful build and push it into another environment. Contrary to other environments, our defining characteristic is not is the system at a high enough quality level to put promoted. We are able to change the question to whether or not there is enough new functionality to justify promoting it. We already know things work, we just have to build enough to brag about!
Engaged Product Owner
At the very top of these layers sits an engaged Product Owner. This is the person with the drive, passion, and subject matter expertise to know that they are building the right system. The layers beneath this help to make sure that the team is building the system right, but the Product Owner is the only one who can define what the right product is. They generally can’t and don’t do the job on their own, relying on input from other stakeholders, who have the ability to see frequent releases of the system. Through using evolving versions, stakeholders are able to see where the product is going, play with new features, and most importantly, give feedback about what they like, what they don’t like, and what is missing. And a good Product Owner can use this feedback to do course corrections on what the team is building.
Thanks for sticking it out this long. I knew this would be a longish blog post, but I felt like this was important to say. I’ve worked with a bunch of teams lately who have tried to “get by” with having separate QA and development teams, throwing code and bugs over the wall at each other, and I’ve watched them waste so much precious development time I just couldn’t take it anymore.
The key to being both efficient and effective with your development time is to waste as little time as possible, but no less, doing manual testing. But this should be mostly exploratory testing, not feature testing that can be automated. And then adopt the practices described in this blog post as a way of eliminating almost every logic defect you’ll ever have.
And at the end of the day, you’ll be left with the right system that works right. And that’s what makes customers happy!