Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

14KB

Spec Driven Development — Chapter-15: 6b - Specification Anti-Patterns: Six Ways to Get It Wrong, Over and Over

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

6b - Specification Anti-Patterns: Six Ways to Get It Wrong, Over and Over Six Defects, All Taken From This App The first lap is over. You watched a spec be born, be questioned, become a plan, become tasks and become code, and you saw the result recorded in the repository. Before the second lap, it is worth stopping at a place the cycle has no dedicated step to teach: what is usually wrong with a spec that looks right. The six patterns in this chapter did not come from a catalog. All of them happened in the To-Do, across the five laps this book walks, and all of them were fixed. That has one consequence for how you read the chapter: some of the episodes belong to laps you have not followed yet, the fourth and the fifth. Each case here is told whole, without depending on the chapter where it appears in full, and when you get there you will recognize the defect before it is named. The way to use this is as a checklist. Once you finish writing a spec, before sending it to clarify , run the six. It takes five minutes, and each of them, once, cost a good deal more than that. A word about the examples. The To-Do's repository only holds the right text, because every defect was fixed. So every bad version you are about to read is a declared hypothesis, built by

subtraction from a real artifact, with the real text beside it and the address of where it lives. No bad example in this chapter is a spec anybody actually handed in.

  1. A Rule Inherited by Reference The symptom: the requirement tells you to apply “the same rule”, “the same criterion”, “as in such-and-such feature”, instead of stating what the rule is. It looks like economy and it is a trap. Whoever writes it has the whole rule in their head at the moment of writing, and the reference preserves perfectly the thing they are thinking. Whoever reads it gets a pointer, and resolves that pointer with whatever they themselves think the rule means. Hypothetical version. Suppose the edit-task spec said only this about the title: FR-003 (hypothetical): The edited title MUST remain non- repeated, on the same criterion as creation. There is nothing grammatically wrong there. The creation rule exists, it is written down, and it is a document from the same project. What the sentence does not answer is whether the task being edited counts in the comparison. The real text. In the repository, after the defect showed up, FR- 003 reads: The edited title MUST remain non-repeated among the other tasks. Keeping the task's own title while editing does NOT count as a duplicate (it is the case of fixing only the

description). And the fifth clarification of the 005-editar-tarefa spec, which you find in Chapter 10.5, records where the adjustment came from: “the earlier wording, ‘on the same criterion as creation’, was ambiguous on this point and made editing only the description fail as a duplicate.” The cost of letting it through. This one got through everything. clarify did not ask, checklist flagged no gap, analyze came out clean, the tests derived from the tasks went green. It was manual validation, with the app open, that caught it. An ambiguous requirement is undetectable by any tool that checks consistency between artifacts, because the spec was consistent with itself: it was consistent and incomplete. The general fix: whenever you are about to write “the same criterion as X”, copy the criterion. If it is too long to copy, that is a sign the rule deserves a place of its own, and then the reference points at that place instead of at another feature. 2. Initial State Left Undeclared The symptom: the spec fixes every possible option and forgets to say which one holds when the person arrives. This one is treacherous because the list of options looks complete. You enumerated everything, reviewed it, nothing missing. What is missing is instant zero. Hypothetical version. Suppose the filter-by-state spec had only this:

FR-001 (hypothetical): The system MUST offer three views: all, open and completed. FR-002 (hypothetical): When a view is chosen, the system MUST show only the matching tasks. The three views are there, the behavior of each one is there. And nobody said what the person sees on opening the app for the first time, which leaves the agent with three equally defensible answers. The real text. This is the only gap in the series that a step of the cycle caught on its own. checklist flagged it, and the 003-filtrar- tarefas spec gained a completeness correction note and a new requirement, FR-005, fixing the default view as “open”. The block is in Chapter 8.5, right after the clarifications: “the initial version of this spec fixed the three views but did not declare which one appears when the screen opens.” The cost of letting it through. Low here, because it was caught early. Had it gone through, the app would open on “all”, which is the default an agent picks when it does not know, and the whole feature would lose its point: anyone who asks for a filter wants to arrive already filtered. The general fix: for every list of options, write the default line. It goes for views, for sorting, for modes, for anything with more than one possible value. The question is always the same: what does the person see before choosing anything? 3. A Verb With Two Owners The symptom: two specs use the same expression to describe different behaviors, and each one is right within itself.

This one does not show up in the spec you are writing. It shows up between yours and another one, written in another lap, which makes it the only one of the six that requires looking outside the document. Hypothetical version. Suppose the delete spec said: FR-002 (hypothetical): On confirming the deletion, the task MUST leave the list definitively. Read alone, that sentence is flawless. Now read it alongside what the complete-task spec already said, three laps earlier: a completed task stays in the list, in the same position, shown as completed. “Leave the list” came to mean two things in the same project, and neither spec has any way of knowing that on its own. The real text. analyze was what caught it, and classified it as a HIGH conflict, the only one at that severity in the whole book. The report is in Chapter 9.5, with item C1 pointing at 004-excluir- tarefa/spec.md FR-002 against 002-concluir-tarefa/spec.md FR-006, and the recommendation to fix at the source: clarify that “definitive” qualifies the act of deleting, as distinct from completing. The correction went into the spec and flowed down the whole chain. The cost of letting it through. High and silent. Two specs that contradict each other produce code that is coherent with each of them separately, and the conflict only shows up when somebody uses both features in the same session. It is the kind of defect that reaches the end user. The general fix: keep a project vocabulary. When you use a strong verb (“leave”, “remove”, “archive”, “cancel”), check whether it has been used before with another meaning. analyze does that cross-check, and it is good that it does, but the cheap moment to find out is while you write.

  1. A Repeated Edge With No Answer The symptom: the spec describes the happy path and the obvious error, and does not say what happens when the operation is repeated, or when it lands on something that is no longer there. Two questions, and they share an origin: the spec thought about the first use and not the second. Hypothetical version. Suppose the complete-task spec stopped here: FR-001 (hypothetical): The person MUST be able to mark an open task as completed. FR-002 (hypothetical): The person MUST be able to reopen a completed task. And what happens when you complete a task that is already completed? The sentence neither forbids nor permits. An agent can treat it as an error, can ignore it, can toggle the state (the worst of the three, because it turns completing into an on/off switch nobody asked for). The real text. The two ends of that edge were closed in different laps, both times in clarify . In the second lap, the fourth clarification of 002-concluir-tarefa , which you find in Chapter 7.5, settled on idempotence: “it is an idempotent no-op: the operation succeeds and the state stays the same, with no additional effect.” In the fifth lap, the fourth clarification of 005-editar-tarefa , in Chapter 10.5, answered the other half, editing something that no longer exists: “a predictable error (error as value), not an exception that breaks the app.” The cost of letting it through. Depends on which half stayed open. An unanswered repetition gives you odd but recoverable behavior. An unanswered operation on something that vanished

tends to become an unhandled exception, which is the app closing in the face of whoever is using it. The general fix: two fixed questions for every operation that changes state. What if it happens twice? What if the target no longer exists? They fit in any spec and almost always reveal a line that was missing. 5. Scope That Grows Out of Convenience The symptom: the spec absorbs the near relative of what was asked for, because “while we are in here anyway”. This is the friendliest of the six, and that makes it the hardest to refuse. Nobody proposes extra scope in bad faith; they propose it because the addition is cheap right now, and it is true that it is. Hypothetical version. Suppose the filter spec had turned into this: FR-00X (hypothetical): Besides filtering by state, the system MUST allow sorting the list by date or by title, and MUST remember the chosen view when the app is reopened. Sorting is filtering's cousin, remembering the choice looks like care for the user, and both would cost little while the filter code is open. The problem is not the cost of building; it is that the feature stopped being reviewable as a single thing, and that persisting the view introduces new state, with a life cycle of its own, inside a spec that was about something else. The real text. Both were refused in the clarifications of 003-filtrar- tarefas , in Chapter 8.5. Sorting: “Not in this feature. The order stays the creation order, inherited from 001. Configurable sorting

is out of scope (YAGNI); if it is wanted, it becomes a feature of its own later.” Persistence: “The filter is an ephemeral view state for the session; on reopening the app, it goes back to the default view.” In the fifth lap the same move refused an edit history, with the same note that it becomes a feature of its own if it is ever wanted. The cost of letting it through. A bloated spec does not fail right away, and that is what makes it expensive. It fails in the review, which nobody finishes; it fails in manual validation, which now has twice the items; and it fails months later, when somebody tries to work out why the filter persists and the sorting does not, and the answer is “because it was open that day”. The general fix: refuse it in the clarification and record the refusal with its reason. Writing “out of scope; if it is wanted, it becomes a feature of its own” costs one line and saves you the whole conversation again three months from now. 6. Fixing at the End of the Chain The symptom: the defect gets patched where it surfaced, in the code or in the task, and the spec goes on saying the wrong thing. This one is different from the five before it. The others are defects of wording; this one is a defect of reflex, and it is what ruins the whole method, because it cuts the link between the documentation and what the software does. Hypothetical version. This one needs no example of text, it needs an example of a gesture. The suite went red because editing only the description failed as a duplicate. The patch fits in one line, and it is the right line:

const isDuplicate = existing.some( (other) => other.id !== id && other.title.trim() === trimmedTitle ); Apply that and you are done: green test, correct behavior, feature shipped. And the spec goes on saying that uniqueness is “on the same criterion as creation”, which is precisely the sentence that produced the defect. The next person to read the spec, or the next agent to work from it, will rebuild the same error. The real text. In both episodes where this nearly happened, the correction climbed back to the source. In the fourth lap's vocabulary conflict, analyze itself recommended it in writing: “fix at the source (the delete spec) [...] Do not patch it in the tasks.” In the fifth lap, the order was spec, then tasks, then code: FR-003 rewritten and Q5 added first, T002 gaining the new case next, and only then the line above. Chapter 10.5 shows that sequence file by file. The cost of letting it through. It is a compounding cost. Every patch that does not climb back to the source widens the distance between what the spec says and what the system does, and that distance is exactly what SDD exists to keep at zero. A spec that lies is worse than no spec at all, because the next person trusts it. The general fix: when you find a defect, before opening the code file, ask which artifact this defect was born in. Fix it there, let the fix flow down, and the code changes as a consequence. The Checklist, in Six Questions

Once the spec is written, before sending it on:

  1. Does any requirement tell you to apply “the same criterion” from somewhere else instead of stating the rule?
  2. Does every list of options have a line saying which one holds on opening?
  3. Does any strong verb in this document already mean something else in another spec in the project?
  4. For every operation that changes state: what if it happens twice? What if the target no longer exists?
  5. Is there anything in here that got in only because it was cheap to add right now?
  6. And, when the defect turns up later: are you going to fix it where it surfaced or where it was born? The first five you answer by reading. The sixth asks a reflex of you, and it is the only one that keeps applying after the document is finished. The second lap starts now, and it is where the fourth question on this list gets answered live, inside clarify .

Powered by TurnKey Linux.