If you observe an average software development team, you’ll notice many things going on at once. You’ll see walls overloaded with story cards representing numerous pieces of work in progress (WIP). You’ll watch developers and testers move to and from meetings throughout the day, and occasionally turn their chairs to ask and answer questions. You’ll observe programmers slamming out a lot of code that accomplishes myriad bits of functionality. You’ll see people create involved automated tests to exercise and verify bits of behavior.
The hustle and bustle create the appearance that the team is getting things done. Apparently, we think multitasking is a good thing — and worse, we believe we’re effective at it.
Todd Herman, founder of the time-management program The 90 Day Year, tells us that “humans aren’t built to multitask.” He mentions the American Psychological Association’s contention that we can lose up to 20 percent of our time when we must switch between two tasks.
In software development, we are at risk of wasting even more time, because we lose more time as tasks increase in complexity. Gerald Weinberg’s oft-cited chart from his book “Quality Software Management: Systems Thinking” shows that you end up with maybe 25 percent of your time being spent effectively if you switch among five projects.
The core reason offered by Herman for the productivity loss created by context switching relates to the concept of flow state — the notion of “being in the zone.” Herman estimates it can take a dozen minutes to get back into a flow state once you’ve been distracted with something else.
The key, then, is to focus on only one thing at a time — OTAAT. Let’s look at some examples of where we can apply this method to regain some lost productivity.
Even when we think we’re working on the same thing, we tend to conflate one concept with others. I often see automated tests that try to verify numerous things:
Bank account test
Create account A
Deposit $50 into A
Verify A has a balance of $50
Request withdrawal of $8 from A
Verify A has a balance of $42
Request withdrawal of $48 from A
Verify that the system presents an error message
Verify A has a balance of $42
It’s seemingly easier to create and run one long test like this. The problem is that when one element fails silently, it often takes longer to figure out why a subsequent verification fails, particularly as the test increases in complexity.
A longer test demonstrates and verifies numerous things. As the test becomes longer, its intent becomes less clear. You can attach only a vague summary description, such as “Bank account test,” to such a test.
OTAAT applies well to tests: Better we split one longer test into multiple tests, each of which verifies one useful behavior. We can name each test with a concise summary of what it verifies:
- Deposit increases account balance
- Withdrawal decreases account balance
- Excess withdrawal generates error
This way, we create more easily understood tests. When any of these one-purpose tests fails, our efforts to uncover the problem are less than with the multipurpose test.
Test-driven development’s cycle of test-code-refactor encourages developers to focus on one element of that short cycle at a time. Write a new test, watch it fail. Write code to make that test pass, ensure that the new test and all existing tests still pass. Clean up the code through refactoring, ensure that all tests still pass.
However, many developers find themselves tempted by bad code as they attempt to get a test passing: “This stuff is tough to understand; let me clean it up a bit.” They then find themselves in a bind — the tests are now failing, they’ve written a good amount of new code, and it’s become hard to separate the changed code from the new code. They waste gobs of time.
When stuck in a mire, the best solution often involves discarding both new and refactored code by reverting to the last known good state. From that clean starting point, the better approach is to refactor first, then write code, or vice versa. Small, discrete steps are almost always more effective.
Don’t refactor and add new code simultaneously. OTAAT!
The crowded story wall is a visible symptom of tackling too much work at once. We learned from lean manufacturing that having multiple tasks in process prolongs time to delivery and actually increases costs. In addition to the aforementioned cost of context switching that high amounts of WIP can encourage, high WIP levels create additional complexities in coordination, tie up resources, and allow problems to remain buried longer.
Perhaps the worst aspect of excessive WIP is that it distracts us from our core goal of delivering value to customers. In turn, we lose our ability to learn how to get good at continually doing just that.
I’ve often promoted pair programming, a collaboration model where two developers sit beside each other to actively build a software product. Developers produce higher quality software as a result because they actively review the code as they go.
For teams that pair, I also recommend frequently switching pair partners — at least once per day. A new person stepping into the mix for a not-yet-completed task provides a third-party review for software produced so far. Typically, by the time a pair marks a task as complete and integrates it into the source repository, it’s too late to remedy any quality problems. A third party can help prevent bad software.
Unfortunately, pair switching represents a context switch and thus engenders a bit of slowdown. We just have to bet that the cost of the context-switching slowdown is less than the cost of low-quality code produced by an isolated pair.
Lately, I’ve promoted mobbing over pair programming. Because everyone in the room works on the same thing at the same time, they don’t lose time to context switching. And in case it’s not obvious, we’re all working on one thing at a time. The effective WIP for a team that mobs is generally one item. We don’t experience the productivity loss of context switching. OTAAT for the win!