Michael Brunton Spall gave a great little talk on his debugging methods at ScaleCamp last Friday. He based it around a procedure called Analysis of Competing Hypthoses (ACH). Boiled down a bit and applied to debugging, ACH instructs you to:
- List all possible causes for a given bug. Get others to offer their own hypotheses. Don't throw any away yet.
- List what's already known as evidence for and against the hypotheses.
- Building on step 2, Decide what extra evidence you need to gather to refute each hypothesis.
- Gather it, and eliminate possibilities until you have one left.
I think the most powerful thing about this process is the way it forces us to falsify and eliminate hypotheses, countering the very human instinct to hunt for evidence to support our hunches. Falsification is efficent: it can often be accomplished with a single piece of inconsistent information, lasering through all the other evidence with a satisfying logical zap.
But these steps may strike you as insultingly obvious. The idea that we brave few, we programmers who craft systems with pure thought-stuff can be helped by a super-simple series of steps is kind of offensive. Or maybe you feel like these sorts of processes are fine for those who need them, but you're OK following your finely honed intincts.
Michael's talk reminded me of a lecture by Dr. Atul Gawande I'd attended the month before. Gawande was promoting his book "The Checklist Manifesto", in which he describes how highly trained surgical teams were able to perform better by following simple checklists.
In Gawande's formulation, a checklist is not a flowchart nor an algorithm. It doesn't aim to turn medical pros into automatons, but instead should be designed to make them think about the right things in the right order. Ideally, the imposition of process supports their mental efforts under stressful conditions and time constraints, allowing them to use their higher faculties for more than remembering what comes next.
After checklists were widely adopted, a survey found that 80% of doctors liked the lists, and admitted they'd saved at least one patient from harm. This left 20% who didn't want to continue using them. However, the same doctors were asked whether they would wish for a checklist to be used if they were going through the operation as a patient, and 94% said yes.
That's a pretty arrogant position to take. These doctors were not arguing that the checklist was flawed and would not help anybody, but that it would not help them. When you start prescribing something for other practicioners of your craft but refuse to use it yourself, you have a problem with humility.
Recognizing that a simple set of steps might help you, a highly trained pro, is difficult. The key thing here is humility. It takes humility to submit to the discipline of a process instead of assuming your way's the best way. It takes humility to say "My initial hunch might be wrong. What else could cause this problem?" It takes humility to bring other people in to offer their views. And it takes humility to seriously entertain hypotheses which you instinctively feel must be wrong.
So with this humility, we might find that processes and checklists offer benefits in many complex situations where we could make incorrect choices. But Gawande heavily emphasises careful design, testing and revision for his checklists. It's obvious that processes could just as easily be constrictive or destructive.
Processes can come into their own is when you're under pressure. When everything turns to custard on the live site, do you ever get that feeling of blind urgency ("Must fix it, must fix it!") which starts to crowd out effective thought about what concrete action to take next?
Following the MBS/ACH process outlined up top, this problem disappears. You find yourself working to track down specific information about what's happening, and uncertainty is mitigated.
Simple processes can also help in everyday coding. Take this old chestnut for example:
- Add a test.
- Run all tests and see if the new one fails.
- Write some code.
- Run the automated tests and see them succeed.
- Refactor code.
- GOTO 1.
A similar sort of humility is required to admit that writing simple unit tests before you write code might make your output significantly better.
However, a lot of what we do doesn't lend itself to checklists or processes. Imagine a checklist for creating a secure webapp:
- Can an attacker use SQL injection?
- Can an attacker use directory traversal?
This list would have to be exhaustive in order to be effective, and an exhaustive list of exploits is impossible. Worse though, the idea that you can press ahead with development of a website, and then worry about security when you're going through the checklist later is fundamentally wrong. This stuff must be written on your heart. You need a security mentality, not a security checklist.
Defining a simple process to support decision-making under conditions of uncertainty can be a powerful tool. No one's arguing that this is a panacea, or should be applied indiscriminately. But having the humility in the face of complexity to admit that simple things can make us more effective is the vital first step to applying them effectively.
Related tags: checklists, complexity, debugging, humility, processes