The story you are about to read is as much about customer responsiveness as it is about software development. The story lies at the intersection of the principles, Deliver Value Continuously and Make Safety A Prerequisite. Finally, the story is based on real-world experiences in a real code base. Let’s begin…
A customer reports a defect. It’s inhibiting them from getting some important work done.
The two programmers on the team, David and Sally, are practitioners of Test-Driven Development (TDD).
The system, which David and Sally inherited from others, has good test coverage because most of it was TDDed. There’s a deployment pipeline to production that enables them to safely deploy to production many times per day.
David is extraordinarily careful to always practice TDD when working on any production code.
Sally has more programming and TDD experience than David. She loves and uses TDD for the vast majority of her code.
The company recently experimented with a Feature Fake. Sally didn’t use TDD for that task. Instead, she hacked the fake feature into the system, pushed it into production, gathered data from it and then removed every trace of it. TDD would have slowed her down and was unnecessary for this throw-away experiment.
Now back to the customer defect…
David looks at the defect. He finds the offending code. It’s some old code that neither David nor Sally wrote. The code isn’t completely without tests, but the test coverage isn’t great. David uses TDD to drive the fix into the code. He improves test coverage and refactors both the production and test code. It takes about two hours for him to finish, commit, run the build and push to production.
Sally looks at the defect and based on the customer’s issue, she’s able to rapidly spot the problem. She can see where the code is missing logic to handle the customer’s issue and the change isn’t hard to make. But the code needs to be refactored and the tests need to be improved. That will take time. She decides she’ll get to that immediately after she first pushes a fix for the customer.
To fix the problem, Sally changes the code and manually tests that her fix works. Her manual test involves performing the same steps that the customer was doing when they experienced the problem. She confirms that the defect is fixed. But did she break anything else? She runs all of the automated tests for the system. They all pass. She feels safe to integrate and push her temporary fix to production. The customer is notified and happy the defect was fixed so quickly.
Now what? Is Sally going to succumb to work pressure and leave her temporary fix in production? Will she ignore the design problems and poor test coverage? Nope. She’s really good and cares a great deal about quality code and tests. Now that the customer issue is solved, she works on a better fix.
Sally backs out the quick fix she made. Now she uses TDD to test-drive the defect fix, including writing better tests and thoroughly refactoring the code. This takes a few hours and afterwards, once the build passes, she pushes the changes to production.
This is the tale of two TDDers. Which programmer would you prefer on your team?
Some would say David. He is safer. He always uses TDD, even if the code will be short lived or when it means it will take longer to fix a customer’s problem.
Some would say Sally. Her problem solving approach is contextual and thoughtful. TDD is her preferred approach to programming, yet she also knows when not to use it. She too is safe, yet her approach to safety doesn’t always involve TDD. She used two techniques to solve the customer’s problem: the first was a short-term fix involving a combination of manual and automated testing while the second used TDD, refactoring and automated testing.
What are your thoughts about these two tales of TDD?
My own preference is Sally’s approach. I don’t like making customers wait. If there’s a safe and fast way to fix a defect, I will use it first. I’ll make a judgement about the work, always ensuring I work as safely as possible.
If I decide to use TDD to fix the defect, it may initially be more like merely Red/Green TDD. I’ll push the fix to production and then come back and refactor aggressively, then push again. If I deem it safe to fix the problem entirely without TDD, I’ll do that, then double back to make an even better fix. In all cases I am testing and ensuring safely, just doing it differently. I’m balancing forces in the intersection of making safety a prerequisite and delivering value continuously.
There are programmers who are neither like David nor Sally. They don’t take the time to write quality code. Instead, they work with haste, taking shortcuts, not refactoring, not applying TDD and maintaining a good suite of automated tests. Such programmers easily succumb to work pressure, creating code that is a maintenance nightmare. Their work style is fundamentally unsafe. Neither David not Sally are like that. However, they approach safety differently.
If you would like more context, see the rest of the story