A Spec is split into Tickets that are each small, and each work end to end. Matt calls them tracer bullets: each one is a thin path through the whole system that you can see working, the way a tracer round shows where it lands.
Vertical, not horizontal
A vertical slice cuts a narrow but complete path through every layer: the database schema, the API, the UI and the tests. A horizontal slice does one layer at a time: all the tables, then all the endpoints, then all the pages.
Only the vertical kind can be shown working on its own. A finished vertical slice can be demoed or checked by itself.
Take a made-up FastAPI booking site, for illustration. "A guest can book a slot", with its table, its endpoint, its page and its test, is one vertical Ticket. "Write every SQL migration" is a horizontal one: nothing works end to end until several more Tickets land.
A vertical slice
A horizontal slice does one layer at a time
the database schema
its table
all the tables
the API
its endpoint
the UI
its page
the tests
its test
Only the vertical kind can be shown working on its own.
A vertical slice cuts a narrow but complete path through every layer
Small enough for one window
Each slice is sized to fit in a single fresh context window. That's what lets every Ticket start clean, with nothing left over from the last one.
The order they go in
Any prefactoring (reshaping the code first so the change is easier) comes first. Matt puts it as: "Make the change easy, then make the easy change."
Each Ticket also lists its blocking edges: the other Tickets that must be done before it can start. A Ticket with no blockers can start at once. The Tickets whose blockers are all done are the frontier, and any of them can be picked up next.
The exception
A wide refactor is one mechanical change, like renaming a column, that breaks call sites all over the codebase at once. No vertical slice of it can pass on its own. So it goes in three steps instead: add the new form beside the old one, move the callers over in batches, then delete the old form once nothing uses it.
In his tracer-bullets article (updated 2026-01-22), Matt names the problem. Left alone, an agent "wants to produce complete solutions all at once". It "just keeps going, building entire layers in isolation, without ever testing whether the critical path actually works."
Matt's Tracer Bullets article (updated Jan 22, 2026): agents build whole layers without testing the critical path. From aihero.dev, 30 October 2026.
His example is a backend. You ask for "a database service with an API that connects to it". The agent writes every endpoint, the models, the error handling, auth, rate limiting and logging. Only then does it touch the database. And then "the connection string format is wrong. Or it's using an incorrect column type. Or the tests don't actually hit the database." Matt borrows a name for this from The Pragmatic Programmer: "outrunning your headlights."
His to-tickets page (updated 2026-08-24) adds the cost of layer-shaped Tickets: "Nothing works until every layer has landed, and each ticket's acceptance criteria have to reach into work that another ticket owns."
Matt also says why this matters more now: "the principles apply harder to AI than they ever did to humans. Context window constraints make the discipline non-negotiable."
Citations (78)
From snapshot/skills/to-tickets/SKILL.md, line 9: “Break a plan, spec, or conversation into a set of tickets: tracer-bullet vertical slices, each declaring the tickets that block it.”
From snapshot/skills/to-tickets/SKILL.md, lines 31–34: “- Each slice cuts a narrow but COMPLETE path through every layer (schema, API, UI, tests): vertical, NOT a horizontal slice of one layer - A completed slice is demoable or verifiable on its own - Each slice is sized to fit in a single fresh context window - Any prefactoring should be done first”
From snapshot/skills/to-tickets/SKILL.md, line 38: “Give each ticket its blocking edges: the other tickets that must complete before it can start. A ticket with no blockers can start immediately.”
The idea is older than agents. Matt says so himself: the tracer bullet "comes from The Pragmatic Programmer", the 1999 book by Andy Hunt and Dave Thomas.
The Pragmatic Programmer (1999), by Andy Hunt and Dave Thomas, as shown on Artima in 2003. From artima.com, 30 October 2026.
In a 2003 interview, Dave Thomas explained the picture. Old artillery used a firing table to work out the aim, then fired and hoped the shell "landed somewhere close to your target." Tracer rounds are phosphorous, so you can see where they land and adjust. He reaches for them "If your target is moving, or if you don't know all the factors". Andy Hunt adds the other side: when you know all the requirements up front and they won't change, working it all out first is "probably the cheapest way to do it." Thomas's summary: "Basically, it all comes down to feedback. The more quickly you can get feedback, the less change you need to get back on target."
Andy Hunt described the code side. You start with "a skeleton application, in which one thin line of execution goes end to end". It "may do nothing more than put a checkbox value into the database as a Boolean." Then you add "one feature at a time", and each one goes all the way through.
Alistair Cockburn has his own name for the idea, a walking skeleton. He first found it "around 1994". He defines it as "a tiny implementation of the system that performs a small end-to-end function." Its parts are incomplete, "but they are hooked together, and will stay hooked together from this point on." He says the Pragmatic Programmers' tracer bullets mean "appoximately the same thing" (his spelling).
Alistair Cockburn's "Walking skeleton" page, dated 1996, as archived in March 2014. From web.archive.org, 30 October 2026.
Cockburn also draws a line worth keeping. A walking skeleton is different from a spike. A walking skeleton is permanent code, built with production coding habits. A spike is thrown away afterward, since it was built with nonproduction coding habits.
A walking skeleton
A spike
built with
production coding habits
nonproduction coding habits
is
permanent code
thrown away
A walking skeleton is different from a spike.
Sources disagree on this point, so here are the dates. Near the end of Matt's January 2026 article, he writes: "Tracer bullets are what /prototype automates — answer questions with throwaway code." In the 2003 interview, Dave Thomas keeps the two apart: "Prototypes are designed to be thrown away." The current Skill files (pinned at Matt's commit of 2026-10-29) are newer, so they decide. to-tickets breaks work into "tracer bullet" Tickets. Each one cuts a narrow but complete path through every layer, tests included, and "A completed slice is demoable or verifiable on its own". The prototype Skill is "throwaway code that answers a question". So on this site, a tracer bullet is not a prototype.
A real example
Matt shows one from his own work, in the same article (updated 2026-01-22). Issue #161 needed a "Reveal in File System" feature for videos. The agent's plan made step 1 the tracer bullet: a backend endpoint, wired up to one location. Steps 2 to 4 added it to two context menus and a dropdown.
In Matt's words: "Rather than building all four UI locations at once, I focused the AI on building just one end-to-end slice: the backend endpoint connected to a single UI location."
Cockburn applied the idea on his first large project. The team's first three-month delivery was what he calls "read a number, write a number." It wasn't pretty, but the server was connected to the database, and the team could build on it.
Where it goes wrong
Slicing by layer anyway. In issue #595 (opened 2026-07-16), a team reported a "26-ticket stack" that "produced ~20 agent runs per closed ticket", about three quarters of it rework. When Matt asked if they used a spec, the reporter re-read to-tickets and wrote: "It's our misuse, and the rule we broke is already in your skill." Their Tickets were "the layers of a single pipeline". Criteria graded work that sibling Tickets owned, and three Tickets blocked each other in a loop. Their Tickets also carried eight to nineteen criteria each: "Mini-specs called tickets."
A path nobody owns. Issue #310 (opened 2026-06-07, still open on 2026-10-30) shows a quieter version. A prep slice built half a read path, and a UI slice assumed the rest "just works." The path between them was "owned by neither". It was filed against to-issues, one of the two Skills later merged into to-tickets. The issue body (drafted by an AI agent, its label says) adds that the quiz step "tends to rubber-stamp the existing cut".
Too many, too small. Matt's page names the opposite failure: "twelve tickets for a three-line change." He calls over-splitting "the most reported problem with this skill".
Matt's check for layer-shaped Tickets is one question per Ticket: "what can I demo when this is done? A ticket with no answer is a horizontal slice."
Try it
In your practice project (set it up first with keeping-track's Try it step if you haven't), build a walking skeleton before the first real feature. For a screen, an app and a database, Cockburn's recipe is "to write “Hello World” in the one and only cell in the new db and get that up through all the layers of the architecture onto the screen." In the yoga-studio repo, that's one row in Postgres, one FastAPI endpoint that reads it, one line on the web/ page that shows it, and one test. Then write the first booking Ticket, and answer Matt's question for it in one line: what can I demo when this is done?
Going deeper's Check questions are asked in Next up.
“Work the frontier: any ticket whose blockers are all done.”
From snapshot/skills/to-tickets/SKILL.md, line 40: “Wide refactors are the exception to vertical slicing. A wide refactor is one mechanical change (rename a column, retype a shared symbol) whose blast radius fans across the whole codebase, so a single edit breaks thousands of call sites at once and no vertical slice can land green.”
From snapshot/skills/to-tickets/SKILL.md, line 40: “First expand: add the new form beside the old so nothing breaks. Then migrate the call sites over in batches sized by blast radius (per package, per directory), each batch its own ticket blocked by the expand, keeping CI green batch to batch because the old form still exists. Finally contract: delete the old form once no caller remains”
From snapshot/web/aihero.dev/tracer-bullets.md, lines 83–92: “The AI builds: - All the API endpoints (GET, POST, PUT, DELETE) - Complete request/response models - Error handling middleware - Authentication logic - Rate limiting - Logging infrastructure Only after all that does it try to connect to the database.”
From snapshot/web/aihero.dev/tracer-bullets.md, line 146: “Rather than building all four UI locations at once, I focused the AI on building just one end-to-end slice: the backend endpoint connected to a single UI location.”
From snapshot/web/github.com/mattpocock-skills-issues-595.md, line 240: “Answering this properly meant re-reading to-tickets in full rather than the two lines I quoted, and most of my report doesn't survive it. It's our misuse, and the rule we broke is already in your skill.”
From snapshot/web/github.com/mattpocock-skills-issues-310.md, line 53: “A deliberately byte-neutral prep slice builds half the read path; the UI slice assumes the rest "just works." The read path that turns stored data into a displayed value is split across A and C and owned by neither.”