25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

24KB

Spec Driven Development — Chapter-13: 6 - Create and list tasks: the first complete loop

  • Source: /library/Spec Driven Development/source-file.pdf
  • PDF pages: 112–128
  • Pages without text: none

6 - Create and list tasks: the first complete loop From the map to the ground: the first feature In the previous chapter you climbed to a high point and saw the whole path from above. Now we come down to the ground and walk it for the first time. The map you kept still holds, word for word; what changes is that, from here on, each step stops being a drawing and becomes a file written to disk. It is worth reopening the map one last time before the first step, because it is inside it that everything that follows happens:

It is the same diagram from Chapter 5; what changes is that this time you step into it. The spotlight of this lap falls on the first step, specify , which had not yet been seen up close because in the previous chapter the whole map was in play. The unit of work, you remember, is the feature: a new, coherent capability the system comes to have. The one you are going to build now is “create and list tasks,” the first of the To-Do, the founding act without which there is nothing to complete, edit, or filter later. It is born as a sentence, travels the whole cycle, and ends as running code, integrated back into main . Before the first command, a short reminder worth gold: have a git repository ready, with main in a stable and clean state. The whole cycle is going to happen inside git, and it only works well if there is firm ground underneath. You don't need to become a git

expert for this, you just need the repository initialized and main with no half-finished work. The first step of the cycle takes care of the rest: it creates the feature branch itself. You will follow the whole path, from specify to merge , and you will see why the output of the first step weighs so much: it is where the feature stops being an idea and gains an outline, and it is that text every later step reads. One feature at a time: the backlog Look at the whole To-Do for a moment. It will need to create tasks, mark as done, edit, filter, and delete. Five capabilities. The temptation for whoever is in a hurry is to specify all five at once, “to get ahead.” It is exactly what you don't do. That queue of capabilities waiting has a name: the backlog, the list of what the system will still gain, in order of priority, without any of them being built before its turn. The backlog is where “complete,” “edit,” “filter,” and “delete” stay kept, named and waiting, while you work on a single feature from start to finish. Why one at a time? Because the feature is the unit of the cycle, and mixing several breaks that unit. A spec that tries to describe create, complete, and filter at the same time becomes a document that doesn't close: the title-uniqueness rule of creation bumps into editing, which also touches the title; the filter asks for states that completion hasn't even defined yet; each answer opens new questions in neighboring features. Specifying one at a time is the constitution's Simplicity and YAGNI (You Aren't Gonna Need It) in action: you solve the problem in front of you, with the scope the right size, and leave the rest in the queue until its time comes. The backlog keeps the other four in view, named and ordered, so you don't have to carry them in your head while working on the

first. No ceremonious document and no special tool: an ordered list of what comes next, plus the discipline of not pulling the next item before finishing the current one. In practice, that backlog can be a plain text file in the repository. I use a draft.md : every time that, in the middle of a specification, I remember a feature the system will need (“it would be good to be able to archive old tasks”), the idea goes into draft.md on one line, and the specification in progress carries on without a detour. That gesture solves both sides of the problem: the idea isn't lost, and it also doesn't invade the spec of the wrong feature. When a lap around the cycle ends, draft.md is the queue the next specification comes out of. A different thing is remembering something that belongs to the current feature (a forgotten edge case, a requirement that was left out): that you don't note down for later; you remind the agent at the step where the hole is, asking it to include what was missing in the spec, the plan, or the tasks, as the case may be. An idea for another feature goes to the draft; a hole in the feature under way goes back into its artifact. Maybe this sounds familiar: specifying everything at once is vibe-coding coming back through the back door, now disguised as a giant document. The backlog is the ordered queue of the slices waiting for the next lap, and it is what holds that temptation back. And which to choose first? “Create and list” imposes itself. It is the base: without creating a task, there is nothing to complete; without listing, creating has no visible effect. The other four depend on this one existing. That is why it is first in the queue, and the rest wait in the backlog, each with its own loop ahead. Spotlight: specify in action

Here the light comes on. The other steps will appear in this chapter, because the cycle runs in full, but it is on specify that the focus falls, because it is where the feature gains shape, and because everything that comes after reads what it writes. You invoke the step by passing, in one sentence, what you want to build: /speckit-specify Create and list tasks in a personal to-do list. The user can create a task by giving a title and a description, and can see the list of al l tasks already created. On creation, two rules hold: the title is required ( it can't be left blank) and the title can't repeat that of a task that alread y exists. Out of this feature: completing, editing, filtering, and deleting t asks, as well as categories, due dates, priority, users or login, and syncing . Notice what that sentence already carries: what you want (create and see tasks), the rules (required and non-repeated title) and, with equal care, what stays out. Saying what doesn't go in is part of specifying well, and not a detail. When it runs, the step does two things. First, it creates the feature branch (in our case, 001-criar-tarefa , starting from main ). From here on, all of the feature's work lives on that isolated branch. Second, it generates a first spec.md , the skeleton of the specification filled in from your sentence, following the tool's template.1 And here is the most important thesis of this step: what comes out of specify is a draft, not the finished spec. The tool organizes your sentence into a structure (scenarios, requirements, criteria), but the content is still yours to review. It gets the skeleton right and guesses at the flesh; you read, correct, complete. That is why, in this first loop, we are going to open that output more calmly than in the following steps.

It is worth seeing what that skeleton brings. For the first user story, specify didn't return just a title: it returned the story, its priority, an independent test, and the acceptance scenarios already written in the Given/When/Then format (Given a context, When someone does an action, Then such a result happens). In the artifact, this appears under the label User Story: the user story you already met in Chapter 2. It is a short narrative, from the point of view of whoever uses the app: what the person wants to do and the result they expect. Don't confuse it with a user journey, which is the map of the path the person travels through the interface, UX/UI territory. In specification, the usual term is story: a small, verifiable slice of behavior, not the whole route through the screen.

User Story 1 - Create a task (Priority: P1)

Acceptance Scenarios:

  1. Given the task list (empty or with tasks), When the person creates a task with a filled-in and unique title, Then the task is created and comes to exist among th e tasks.
  2. Given the intent to create a task, When the title is given blank ( empty or only spaces), Then the creation is refused with an error result explaining that the title is required, and no task is created.
  3. Given an already existing task with a certain title, When the pers on tries to create another task with that same title, Then the creation is refused with an error result explaining that the title already exists, and no new task is created. The Given / When / Then markers stay in English because they come from the template, but nothing in the document needs to be translated: you simply read them as Given / When / Then. In the

first scenario, for example: Given the task list, When the person creates a task with a filled-in and unique title, Then the task is created and comes to exist among the tasks. This is more than a title and much less than the final truth. The skeleton got the shape right (the three scenarios that matter are there, in the right format), but it is you who checks whether they say what the problem demands. It was reading these scenarios that made it clear, for example, that something the original sentence didn't say was still to be decided: do the tasks need to survive closing the app, or is it enough for them to exist during the session? The draft exposed the question without answering it. Reviewing that draft is different from rewriting it from scratch: it is running your eye over each part asking “is this true for my problem?". You read each requirement and check whether it matches the rule you have in your head; you flag what the skeleton assumed and you don't want; you add what was missing because your sentence didn't say it. That review is the iterative conversation Chapter 5 recommended, applied to the first step: point out each correction to the agent, ask for the adjustment in the spec, and, if you changed a lot, run /speckit-specify again over the result, without guilt, because repeating a step to revise it is normal use of the flow. It is a job of critical reading, quick when the feature is small like this one, and it is where your knowledge of the product, which the model doesn't have, enters the specification. Optional box: why a draft and not the final version? An AI model doesn't know your product: it knows your sentence. The skeleton it fills in is a plausible hypothesis about what you meant, not the truth about what you need. The work of

reviewing the spec is where your knowledge of the problem enters, and it is exactly that work Chapter 7 is going to light up, with the clarify step. The anatomy from Ch. 2, actually filled in In Chapter 2 you saw the anatomy of a good specification in the abstract: the problem and the intent, the scope, the non-goals, the scenarios, the rules, and the acceptance criteria. Now the same anatomy appears filled in for a concrete feature, like opening the hood of a car that runs after studying the engine diagram. We won't re-explain each part, we will recognize it in the real artifact. The scope is minimal and explicit: create a task with a title and a description, and see the list of all of them. The central entity too:

  • Task: represents a to-do recorded by the person. Essential attributes: a title (required and non-repeated among existing tasks) and a description (fre e text, optional). The business rules are two, and the spec fixes them as verifiable requirements. The FR prefix that numbers each one comes from Functional Requirement: a testable statement of what the system needs to do. The numbering (FR-001, FR-002…) only serves to reference each requirement without ambiguity across the other artifacts. The spec has seven requirements in total, and throughout this chapter we will look at each one at the moment it matters, instead of dumping the whole list at once. FR-001 is the basic

capability (create a task from a title and an optional description), which already appeared in the scope above; listing and persistence come further on. The two rules that interest us now are the constraints that come right after:

  • FR-002: The system MUST refuse the creation when the title is blank (em pty or made up only of spaces), returning an explicit error result that identifies the req uired title as the cause, without creating the task.
  • FR-003: The system MUST refuse the creation when a task with the same t itle already exists (compared after trimming spaces at the ends), returning an explicit error r esult that identifies the duplication as the cause, without creating the task. Notice something subtle and important in those two requirements: they don't say “throw an error” or “raise an exception.” They say “returning an explicit error result.” That is the To-Do's constitution reflected in the spec: the principle of error as value, which you fixed back there, showing up here as the natural way to describe the two predictable failures of creation. The spec didn't invent exotic failure flows; it just named the two cases the rule itself produces and said both are expected results, never accidents. One more requirement makes this explicit for any reader:
  • FR-004: The system MUST represent the predictable creation failures (mi ssing required title; duplicate title) as explicit success or error results, and MUST NOT signal them as exceptions across layers.

And the non-goals, which so many specs forget, are written in so many words:

  • Explicitly out of scope: categories, due dates, priority, users/login, an d syncing (YAGNI). The acceptance scenarios already appeared in the previous step's skeleton, written in Given/When/Then: it is where each abstract rule becomes a concrete situation that can be staged. And the acceptance criteria close the anatomy by turning each rule into a measurable result that a test can verify with no room for interpretation. They come prefixed with SC, for Success Criteria, numbered like the requirements (SC-001, SC-002…); again, we show only the ones that matter for this section's rules:
  • SC-002: An attempt to create a task with a blank title is refused with a clear error result, and the task count doesn't change.
  • SC-003: An attempt to create a task with an already existing title is r efused with a clear error result, and the task count doesn't change.
  • SC-005: Tasks created in a session keep appearing in the list after the app is closed and reopened. Notice that each criterion is observable from the outside: “the task count doesn't change,” “keep appearing.” None of them talks about code, class, or database; they talk about what the person using the app can verify with their own eyes. It is the what, never the how, taken to the point of becoming an acceptance test that either passes or doesn't.

Each part of Chapter 2's anatomy has a counterpart here: the problem in the task story, the scope in the entity and the scenarios, the rules in the FRs, the non-goals in the backlog queue, the acceptance criteria in the measurable SCs. The specification stopped being a mold and became the document of a feature that exists. What is spec and what is plan : the boundary There is a question that decides whether your spec will age well or turn into a straitjacket: what goes into the specification and what is left for the plan ? The rule you already know from Chapter 2 (the spec describes the what and the why; the plan decides the how); what this first loop adds is the practical test of applying it to real sentences. Take some candidates and classify each one: “The task needs a title” → spec. It is the what: a rule of the problem, true in any technology. “The list shows the existing tasks, from oldest to newest” → spec. Again the what: the observable behavior, independent of implementation. “Store the tasks in localStorage " → plan . It is the how: a decision about the storage medium. “Use React and Zustand” → plan . Pure how: language, framework, state pattern. A mental test resolves any doubtful sentence: would it still be true if you swapped the entire technology? “The task needs a title” keeps holding in React, in Flutter, in a terminal app, or on paper: it is spec. “Use Zustand for the state” disappears the instant you switch frameworks: it is plan . If the sentence

survives the stack swap, it describes the problem; if it dies along with the technology, it describes the solution, and its place is in the plan. See the real specification following that boundary to the letter. About persistence, it says the what and declares, in its own text, that the how is a decision of another step:

  • FR-006: The system MUST preserve the created tasks between sessions of use, so that they keep appearing when you see the list after the app is closed and reopened. The s torage medium is decided in the Plan. The final sentence of that requirement is the boundary drawn inside the document: “the storage medium is decided in the Plan.” The spec requires the tasks to survive closing and reopening, but doesn't say a word about a file, database, or localStorage . That keeps the specification technology-agnostic, as the constitution's Technology Agnosticism demands, and leaves the ground free for the plan to choose the stack without rewriting the problem. Optional box: why does that boundary matter so much? When the “how” leaks into the spec, you tie the problem to a solution too early. Switch the database, the framework, or the state pattern, and a contaminated spec has to be rewritten along with it. A spec that talks only about the what and the why survives all those switches, because it describes something that didn't change: what the user needs.

That boundary isn't an invention of SDD; it just names something that always existed in software development. Think of the conversation between a systems analyst and the client who commissioned the system. The client talks about their problem (what they need to record, which rule can't be broken, what counts as done) and almost never knows, nor cares to know, whether it will run on Postgres or SQLite, in React or in Flutter. Stack is the domain of whoever builds; the analyst themselves, when they know the subject, understands it at a high level. The spec is the client's side of that conversation: the what and the why, in the language of the problem. The plan is the developer's side: the how, in the language of the solution. In a small project like the To-Do both voices are yours, but in a more complex environment they are different people, in different roles, and keeping the spec technology-agnostic is exactly what lets those two voices talk without one invading the other's territory. The rest of the loop, at a follow-along pace With the spec reviewed, the spotlight goes off and the other steps pass, each reading the artifact the previous one wrote. Here the pace is follow-along: what ran, what went in, what came out. clarify came right after the spec, asking about what it had left ambiguous. Two questions, and their answers were written back into the spec itself:

  • Q: Should the created tasks survive closing and reopening the app, or is it enough for them to exist during the session? → A: The tasks are preserved between sessions of use.
  • Q: In what order does the list present the existing tasks? → A: In order of creation, from oldest to newest.

That step will have its own spotlight in Chapter 7; for now, it is enough to see that it exists to resolve ambiguity before it becomes a wrong decision in the plan. plan read the clarified spec and decided the how: React with TypeScript in the interface, Zustand for the state, localStorage behind a repository layer for persistence. It is here too that the constitution check runs, and the plan came out reflecting the To- Do's constitution: the business rule isolated in a domain layer, independent of the interface; the error as value materialized in a result type; the layers separated, with composition happening only at the edge. checklist validated whether the spec and plan were complete and clear before becoming tasks. tasks broke the plan into an ordered list of small tasks and, faithful to the constitution's TDD, each implementation block comes after its test. You can see this literally in the list, in the pair that handles the creation rule:

  • T009 test/domain/create-task.test.ts: tests for CreateTask (success; em pty title; only spaces; duplicate; optional description). Fails first.
  • T010 src/domain/usecases/create-task.ts: implement until T009 passes. The test task is numbered before the code task, and it says “fails first”: you write the test, watch it fail for lack of the implementation, and only then write the code that makes it pass. The constitution asked for TDD; tasks translated the principle into execution order, and implement followed that order. The same happened with the layers: the plan put the business rule in a domain layer that imports nothing from the interface, and localStorage in a data layer behind a repository, so that swapping React for something else, or localStorage for a database, doesn't

touch the rule. Why the dependency runs in that direction and not the opposite one belongs to FOCUS Architecture (2026, https://books.kodel.com.br/en/books/focus/), the second volume in this trilogy, which answers where each rule lives and why dependencies point inward. You do not need it to follow this lap: what matters here is that the decision came out of the plan, not out of improvisation. The constitution wasn't reopened for discussion; it appeared, already applied, inside the artifacts. analyze checked the consistency among the three artifacts. And implement ran through the tasks until the code existed for real, with the tests green. Between one step and the next, the /clear : that habit of clearing the agent's context at the end of each step, safe for the reason the previous chapter fixed. The clarified spec is in the file; the plan is in the file; the tasks are in the file. Between the plan and the tasks , for example, you can erase everything the agent “knew” about the plan discussion without worry, because tasks doesn't need that conversation: it needs the written plan.md . At the end of that sequence, the feature that started as a sentence is code that runs: it creates tasks, refuses the invalid ones with a clear error result, lists what exists in the right order, and survives closing and reopening the app. Commit, merge, and the bridge to Ch. 7 The feature is ready on the 001-criar-tarefa branch, with its tests passing. What is missing is the gesture that closes the cycle: integrating back. You commit the work and do the merge into main . The feature branch has done its job; main goes back to being

the stable version, now with one more capability than it had before. The repository is sound, and the To-Do's first feature exists for real. That gesture carries weight beyond the symbolic. While the feature lived on the branch, it could be half-done without getting in anyone's way; on entering main , it becomes part of the version considered good, and that is why it only crosses that door with the tests green. main keeps being what it always was: the place where nothing is half-done. It is the whole lap around the diagram: main → branch → cycle → merge → main . We left a stable main , opened a branch for a feature, traveled the cycle from specify to implement , and came back to a stable main again. The map from Ch. 5 stopped being a drawing and became history written on the branch. And the next step? The backlog is there, waiting. The next feature is completing a task: marking as done what you recorded here. The cycle will be the same, from specify to merge ; what changes is the step under the light. In Chapter 7, the spotlight shifts one square ahead, to clarify , and you can already feel why it deserves its own spotlight: it was clarify that caught the ambiguity of persistence (survive closing the app, or not?) that the spec, on its own, had left open. Here that step passed quickly, in the follow-along; there, it becomes the center. Same path, new spotlight. We keep walking.

Footnotes Command names, the order of the steps, and the exact form of invocation may evolve between versions of spec-kit; the excerpts in this chapter were generated with version 0.11.8. For details that change per release (flags, subcommands, internal paths), check the official spec-kit documentation instead of fixing them from memory.

Powered by TurnKey Linux.