Many decades ago, people often had a stack of equipment for playing music and video. Typical stacks included a cassette player, compact disk player, VCR or DVD player, television, cable box for watching cable television and maybe even a turntable for listening to vinyl records. You’d need an audio/video (AV) receiver to take all of the audio and video sources from the devices and send sound to your speakers and video to your TV. Typical AV receivers enabled you to tune into the radio, adjust volume on every device, control bass or treble levels and act as a master power switch for every device.
Integrating all of these devices to work correctly was never easy. You’d have a bunch of power cords, audio/video cables and numerous input and output jacks on the back of every device. Which cord was the correct cord and which input and output jack was it supposed to be plugged in to?
In 1998, when I moved from New York to an apartment in San Francisco, I went through the painful process of connecting all of my devices. Once they were stacked, I attached all of the cords and cables, hoping I’d selected the correct input and output jacks. Mistakes were common. Why wasn’t sound coming from the compact disk player? Why was the VCR not sending any signal to the TV? It would take several maddening hours to sort out this mess.
But that changed in early 2001 when I moved to a house in Berkeley. I still had all of the same audio and video devices, but I had changed. I’d become a serious practitioner of a software design technique called Test-Driven Development (TDD) and it would influence how I integrated all of my audio/video equipment.
TDD is far different from doing a bunch of work up front and later testing whether everything works. With TDD, you test as you work, driving the evolution of a solution by means of small, safe steps. Imagine you don’t have a calculator on your computer and want to write the calculator software. Where do you begin? TDD asks you to start with a test, like asking your calculator to add 2 + 2 and expecting it to compute 4. You specify this example as an automated test, run it and naturally it fails because the software for the calculator doesn’t yet exist. Now you write the code to make your fledgling calculator capable of simply returning the correct result from adding 2 + 2. You re-run your test and confirm that it passes. Now you’ve got a passing test and the early, extremely primitive beginnings of a calculator. Before you move on to writing your next failing test, you refactor, which means to make design improvements without changing the behavior of your code.
TDD would help you produce every feature of your calculator, always beginning with a failing test, getting it to pass and then refactoring to improve the design. As you produce more and more tests via TDD, you end up with a suite of automated tests that you may run at any time to confirm whether everything works. This is your safety net and it enables you to develop software with speed and confidence. TDD is a paradigm shift in software development, a safe design process that enables programmers to rapidly evolve systems with few-to-no defects.
By early 2001, I had over three years of real-world TDD experience. It marked a profound shift in how I designed and developed software. Standing in front of my stack of un-integrated audio and video devices in my new home in Berkeley, I wondered whether TDD could help with the integration problem?
I began with the AV receiver. Since it wasn’t plugged in, it couldn’t play the radio or perform any functions. I had a failing test. I plugged it in, attached the antenna and confirmed that the radio worked. Passing test! Then I adjusted the antenna cable so it was neat and tidy. Refactoring. I had just completed a full TDD cycle. Next, I focused on the cassette tape player. It also didn’t work, so I plugged it in and attached it’s power cable and cords to the receiver, then tested whether it worked. It did not. I had selected the wrong input jack. After correcting my mistake, I tested and confirmed that all functions on the cassette tape player worked and that the receiver controlled it correctly, including acting as the master switch for its power. I adjusted the cable and cords to be nice and tidy, then repeated the TDD process with each and every device. To my delight, within about 15 minutes, everything just worked!
TDD, which originated in the software field, had just helped make a normally painful and error-prone hardware integration process quick, easy and graceful. In 2001, that was a revelation to me. Over time, I’ve come to see that smart process techniques in software development have broad applicability across domains. TDD is one of them.