Tracer bullets for LLM work
I have been thinking about tracer bullets from The Pragmatic Programmer.1
The idea is simple: before you build a whole feature, send one small piece through the real system. Start in the UI, hit the API, write to the database, and show the result back to the user. That first version can be ugly. It can skip edge cases. It only needs to prove that the path works.
That was useful before AI. It matters more now, because LLMs can create a lot of code before you have proof that any of it works together.
LLMs like to work sideways
When I ask an LLM to build a feature, its default plan is usually horizontal. First it builds the database. Then it builds the API. Then it builds the UI. Then it adds tests. Somewhere near the end, we finally run the feature and see if the pieces connect.
That is a bad feedback loop for a junior engineer. The code can look right in each layer, but still fail as a feature. The migration can be fine. The route can be fine. The component can be fine. Then you click the button and nothing useful happens.
This is where AI-assisted coding gets expensive. Not because the model is slow, but because the review gets too big. A large horizontal change asks you to understand migrations, routes, helpers, components, state, and tests before you have seen one real user path work.
That is too much to trust at once.
Tracer bullets make the model prove it
A tracer bullet is a tiny vertical slice. If the feature is notes, the first ticket should not be “build the notes database layer.” A better first ticket is: “A logged-in user can write one plain-text note, save it, refresh the page, and see it again.”
That one ticket crosses the stack. It uses the database only as much as needed. It adds one route if needed. It adds the smallest UI that can prove the behavior. No tags. No search. No sharing. No polished empty state. Just one note, saved and shown again.
Now the LLM has to deal with the real seams in the app: auth, routing, data shape, errors, and the assumptions hiding between files. You also get a diff small enough to review without guessing.
Each tracer bullet can be a ticket
This changes how I want to write AI coding tasks. Instead of one big ticket called “Build notes,” I would rather split the work into vertical tickets:
- User can create one note and see it after refresh.
- User gets a basic error when the note is empty.
- User can delete one note.
- Notes only show for the user who created them.
- The page gets the final empty state and loading state.
horizontal ticket
tracer tickets
Each ticket has a working path. Each ticket has a smaller prompt. Each ticket has a smaller diff. Each ticket gives the model feedback from the real system instead of feedback from its own assumptions.
That last part matters. LLMs are good at continuing a shape. If the first layer is wrong, the model will keep building on it. A tracer bullet makes the wrong shape show up earlier, while the cost of changing it is still low.
The foundation is still yours
AI can write code faster than I can type it. That does not mean I should ask it for more code. It means I need to get better at shaping the work.
For junior engineers, this is a big deal. The goal is not to make the LLM produce the largest diff. The goal is to make it produce a small change you can run, understand, and correct. If you cannot explain the change, the model is driving and you are just watching.
Tracer bullets help you stay in control. They force integration early. They turn a vague feature into a sequence of things you can run. They keep the feedback loop short enough that you can still learn from it.
That is the foundation I want when working with LLMs: small prompt, small diff, real path, fast feedback.
Do not ask the model to build the whole floor before checking whether the door opens. Fire one tracer bullet. Then aim the next one.
Footnotes
-
Andrew Hunt and David Thomas, The Pragmatic Programmer. ↩