How are houses built? Do you finish the bathroom, with tiled floors, a sink, and even a shower curtain, before you even start on the master bedroom? Of course not. You prepare the site, construct framing for the entire house, finish the exterior, install infrastructure (plumbing, electrical, insulation, etc.), and continue to add detail and flesh out different parts of the house until it is complete.
In this post, I will make the case that this is not only the right way to build a house, but the right way to write software as well. Like a house, software is composed of different parts or components. These components need to come together as soon as possible to facilitate good design, prevent unnecessary rework, and enable a steady, stable progression.
There are two primary approaches when it comes to adding functionality to an application. These should be treated not as two discrete options, but as two ends of a spectrum. In reality, you may use or encounter approaches that fall somewhere in between them.
Both approaches assume you have divided your application and/or functionality into components. Separating your work into components may be as simple as frontend and backend for simpler functionality. It could be much less obvious and involve more effort on your part to determine what makes sense. Either way, working with smaller tasks and units of functionality makes the end result come easier.
The first approach, what I will call the sequential approach, involves completing components one at a time, and to completion (or close enough), until you finally put the pieces together at the end. This approach is easy to use, as it requires less mental strain to keep one component in mind than to try to think about the functionality as a whole; especially if the components are spread out across different layers or languages. It also gives you a feeling of progress, as you may feel you can confidently mark a component as done once you finish it and move on to the next one.
Delaying the integration of components is risky. No matter how well you may have planned, there are just some issues that can’t be foreseen until implementation. What happens if the components don’t interact the way you thought they would? What would have been a trivial change earlier on in the process is now much more expensive. Components have been fleshed out and therefore more code needs to be changed.
False Sense of Progress
As a result of delaying the discovery of such problems, you actually are creating a false sense of progress. How can you actually be sure a component is finished if you don’t know how well it will integrate with other components, or if it will at all?
Lastly, when utilizing the sequential approach, you don’t have anything actually working until the very end. For an application, this is unacceptable, as you may need something to demo for your client or supervisor. On a smaller scale, the feedback you get from seeing something work early on is invaluable. Using this approach creates a very loose feedback loop. Writing faulty code now will result in problems much later, and tracking down the culprit will be much more difficult after other code has been written.
Tracer Bullet Programming
In The Pragmatic Programmer: From Journeyman to Master, the authors describe a method for developing software called tracer bullets. In essence, the tracer bullet method involves implementing a new application end-to-end to test every layer or component’s interaction. The key benefit to this method is to get quick feedback on a variety of factors. It serves as proof that the architecture is compatible and feasible, as well as providing a working, demo-able skeleton to work with very early on in the development process.
In practice, I believe this approach can work at both high and lower levels; both when creating a new application from scratch or adding new functionality to an existing application. You can accomplish this by implementing the most basic, fundamental part of the new functionality and improving it incrementally as you flesh out each component.
See the Big Picture Early
It’s hard to think about all of the layers and components and how they interact all at once. By implementing a small part of each one and connecting them early on, you force yourself to do this. This brings the discovery of potential problems with your overall design much earlier in the process. This means fixing them is much easier, as there is not as much code to fix and you can adjust your design accordingly going forward.
Get a Working Version Early
Coding without seeing your code in action is often like coding in the dark. With no immediate feedback, you are further and further obfuscating the causes of potential issues you may face down the road. Using the tracer bullet approach means you get a working version early, though the functionality may be basic. This means you have something to show for your work early on. Additionally, the working code you write acts as a “skeleton”, where all further changes are improvements to working functionality rather than a step toward it.
This is the agile beauty of the tracer bullet approach. It allows for adaptive planning, continual improvement, and functionality that is deliverable (in a sense) in its current state at any moment.
Progress Steadily and Confidently
Unlike the sequential approach, the tracer bullet approach allows you to be confident in your overall progress at a given time. Because you have seen all of your code in action, you can be more confident that the work you have done so far is adequate and should require minimal rework.
By leaning toward the tracer bullet approach rather than the sequential approach, you can identify problems early, get something working early, and progress more steadily and confidently. This approach is invaluable not only when starting on a new application, but can also be applied when adding functionality to an existing application.