Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

19KB

Spec Driven Development — Chapter-06: 2b - Requirement Language: Writing What the AI Executes Without Guessing

  • Source: /library/Spec Driven Development/source-file.pdf
  • PDF pages: 43–56
  • Pages without text: 55

2b - Requirement Language: Writing What the AI Executes Without Guessing The Blueprint Is Right, the Handwriting Is Not The previous chapter showed what parts a specification has: the problem, the scope, the scenarios, the rules, the acceptance criteria, the edges. You know how to assemble the document. What is missing is the layer underneath, the one that decides whether each sentence inside it gets executed the way you meant or interpreted however it landed. Two specs can have exactly the same sections filled in and yield different code. The difference is not in the structure, it is in the shape of each sentence. “The system must validate the title” has a subject, a verb and an object, fits in the functional requirements section, and says almost nothing: validate against what, when, and what happens when validation refuses. Compare it with what the To-Do's create-task spec actually says: FR-002: The system MUST refuse creation when the title is blank (empty or made only of spaces), returning an explicit error result that identifies the required title as the cause, without creating the task.

Same section, same part of the blueprint. The second sentence has a trigger (“when the title is blank”), has the thing to do (“refuse creation”), has the shape of the response (“explicit error result”) and has the negative guarantee (“without creating the task”). None of those four is optional for whoever implements it, and an agent handed the first version will invent all four. That is what this chapter is about: the shapes requirements engineering invented so that a sentence cannot be read two ways. There are four, each born from a different problem, and none of them was created to talk to an AI. All of them were created because humans were already reading requirements in incompatible ways long before agents existed. What changed is that the cost of the misunderstanding now falls on an executor that never asks for clarification on its own. Where Specifications Come From A paragraph of history is worth it here, because it explains why the four notations are so different from one another. The specification was born big. In the 1970s and 1980s, the requirements document was a single volume, written before any code, and the standard that formalized it, IEEE 830, went as far as fixing a recommended table of contents with dozens of sections.1 It was the waterfall of Chapter 1, put on paper: a long document, approved by signature, and a project that only started afterwards. The problem was not the rigor, it was the size of the loop. When the first screen appeared, two years later, half the requirements had aged out. The agile reaction shrank the document until it nearly vanished. The user story on a card, with the conversation as the complement, was the answer to the thousand-page volume. It

worked for pace, and it opened another hole: a card that says “as a user, I want to filter my tasks” cannot be verified. The ambiguity the giant volume hid by excess, the card hid by absence. The four notations in this chapter are attempts at the middle ground: the precision of a standard in the size of a card. They came from distinct traditions, aerospace, automated testing, programming language design, and that is why they serve distinct purposes. None replaces the others, and the To-Do's spec uses three of the four without anyone ever announcing it. Before Anything: Functional or Technical There is a division that comes before any notation, and it decides where a sentence lives before it decides how the sentence is written. A functional specification describes observable behavior: what the system does, for whom, under what condition, and how you know it worked. It is written in the vocabulary of the problem. If you swap React for Vue, or localStorage for a remote database, the functional specification stays true word for word. A technical specification describes construction: what layers exist, what interface each one exposes, what data structure holds the operation up, where state lives. It is written in the vocabulary of the solution, and it dies along with the stack choice. Chapter 2 already fixed the boundary between spec and plan; this is the same boundary seen from the sentence. What matters here is the practical test, which works well when you are mid-draft and cannot tell whether that paragraph belongs in the spec: swap the technology in your head and reread. If the sentence still makes sense, it is functional. If it turns to nonsense, it is technical, and its place is the plan.

Apply it to the To-Do. “Created tasks are still there after the app is closed and reopened” survives swapping anything. “Tasks are written to the browser's localStorage " does not survive even the decision to build a phone app. Both sentences are true about the same system, and only the first is a requirement. The second is a plan decision, and the first feature's plan.md records it exactly that way, with the note that the domain knows nothing about localStorage . Confusing the two is the origin of half the bad specs in existence. A spec that opens by saying “create a POST /tasks endpoint that writes to the tasks table” left nothing for plan to decide, and tied the feature to an architecture before anyone asked whether it was the right one. EARS: The Syntax That Will Not Let the Condition Stay Implicit EARS, the Easy Approach to Requirements Syntax, was born in aeronautical engineering, at Rolls-Royce, and was presented in 2009 at a requirements engineering conference.2 The problem it solved was the opposite of ours: jet engine requirements, written by dozens of people, reviewed by auditors, and one of them misunderstood cost certification. The solution was to restrict the grammar. Not the vocabulary, the grammar: every requirement has to fit one of five shapes. Ubiquitous, for what always holds, with no trigger: The system MUST preserve the creation order of tasks. Event-driven, opened by when, for what happens in response to something:

When the person creates a task with a filled-in, unique title, the system MUST register it and start showing it in the list. State-driven, opened by while, for what holds during a continuous condition: While the active view is “open”, the system MUST show only open tasks. Unwanted behavior, opened by if, for what the system does when something goes wrong: If the title provided is blank, then the system MUST refuse creation and return an error that identifies the required title as the cause. Optional, opened by where, for what only holds in a configuration or variant: Where local storage is unavailable, the system MUST ... The fifth shape is the one that shows up least in a small project, and the To-Do has no requirement of that kind. The first four cover everything the five features needed. What the restriction buys is one single thing, and it is a big one: the triggering condition is never left implicit. A requirement that is not ubiquitous has, mandatorily, a clause opening the sentence that says when it holds. You cannot write “the system validates the title” and move on, because that sentence is none of

the five shapes: either it is ubiquitous, and so it validates always, including while listing, which is false; or it has a trigger, and the trigger has to show up. Notice that the To-Do's spec never uses the words “ubiquitous” or “event-driven” anywhere, and still keeps the discipline. The FR-002 that opened this chapter is pure unwanted behavior: condition, action, shape of the response, negative guarantee. FR- 005 of the filtering feature (“the system MUST open, on startup, in the default ‘open’ view”) is event-driven, with “on startup” as the trigger. You do not need to announce the notation to reap its benefit. You need to know it so you notice when the sentence you just wrote fits no shape at all, which is the sign that it is incomplete. One vocabulary detail the standard brought along, and one the To-Do's spec uses on every line: the MUST in capitals. It comes from the RFC tradition and separates obligation from suggestion. MUST is what the system has to do; MUST NOT is what it cannot do under any circumstance; SHOULD is a recommendation, and it is precisely because it is weak that it hardly appears in a good spec. If something is a SHOULD, ask why it is in the spec. Given/When/Then: Behavior as a Scene Given/When/Then came from somewhere else. It was born in BDD, Behaviour-Driven Development, formulated by Dan North out of the practice of TDD, and the original intent was pedagogical: people learning TDD did not know where to start writing a test, and writing the sentence before the code unblocked them.3 The format caught on because it serves both ends. People who do not program can read it and disagree; the test tool can execute it.

The shape has three parts, and each one answers a question: Given: what state the world is in beforehand. It is the setup, not the action. When: the single gesture that triggers the behavior. Then: what became true afterwards. A real scenario from the To-Do's first feature, copied from the spec: Given a task that already exists with a certain title, When the person tries to create another task with that same title, Then creation is refused with an error result explaining that the title already exists, and no new task is created. Three things in that scenario deserve attention. The first is that the Given declares the initial state, and declaring initial state is where specs fail most. The second is that the When has a single action; a scenario with two Whens is two badly separated scenarios. The third is that the Then asserts two things, the error and the absence of a side effect, and the second is the one that actually catches the defect. A system that shows the error message and creates the task anyway passes half the scenario. The relationship with EARS is not one of competition. EARS gives shape to the rule; Given/When/Then gives shape to the example that proves the rule. A good spec usually has both, and the To- Do's does: the functional requirements section is EARS without saying the name, the acceptance scenarios section is Given/When/Then saying the name out loud. When they disagree, either the rule is wrong or the example is wrong, and finding that out while reading costs one conversation. Finding it out later costs a whole lap.

Design by Contract: What Holds Before, After and Always The third notation comes from programming, not documentation. Design by Contract was created by Bertrand Meyer along with the Eiffel language in the 1980s, and the idea is to treat every operation as a contract between the caller and the executor.4 Three clauses: Precondition: what has to be true for the operation to be callable at all. The caller's responsibility. Postcondition: what the operation guarantees will be true when it finishes successfully. The executor's responsibility. Invariant: what is true before and after, always, and which the operation has no license to break. Why does this matter in a spec, if it is an idea from language design? Because the three clauses are questions most specs forget to answer, and the agent answers them on its own when they are missing. Take the operation of editing a task, the To-Do's fifth feature, and read its spec through that lens: Precondition: a task with the given id exists. The spec says that, and it also says what happens when the precondition fails (error as value, not an exception), which is the decision that turns a precondition into specified behavior instead of into a crash. Postcondition: the task's title and description become the ones provided, and the title is still required and not repeated among the other tasks. Invariant: the task's position in the list and its creation date do not change. That holds before, during and after any edit,

and the spec states it as a requirement of its own, FR-005. The invariant is the easiest one to forget and the most expensive one to discover late, because it belongs to no operation in particular: it belongs to the system. Nobody spontaneously writes “editing must not reorder the list”, because reordering never crosses the mind of someone thinking about editing. It crosses the mind of whoever implements it, when the simplest way to save the change is to remove and reinsert. One question worth asking of every spec before closing it: what has to stay true once this feature exists? The answers are the invariants, and each one deserves an explicit line. ATDD: The Acceptance Criterion Written First The fourth one is less a notation and more an order of work. ATDD, Acceptance Test-Driven Development, is the practice of writing the acceptance test before building, together with whoever asked for the feature, and using that test as the definition of done.5 The TDD Chapter 1 introduced does this at the level of a unit of code; ATDD does it at the level of the feature, and the difference in level changes who takes part: the unit test is written by whoever programs, the acceptance test is written by whoever knows what the thing needs to do. In the flow this book walks, ATDD shows up without that name in two places. The success criteria of the spec, which are the verifiable statements about the final result. And the quickstart.md each feature produces, with its numbered list of manual validations that someone runs with the app open in front of them.

Notice what is specific about that order. Writing the criterion after building is describing what got finished, and that never fails anything, because the criterion is born molded to the result. Writing it first is taking on a commitment that can fail. It was item 4 of the fifth feature's quickstart.md , “edit only the description”, written before the code existed, that failed the implementation and forced the spec to change. Had that list been drafted afterwards, it would have had five green items and one live defect. Which to Use, and When The four coexist in a single spec, and each occupies a different place in the document. Notation Answers Where it goes in a spec Sign that it is missin EARS Under what condition the rule holds Functional requirements A requiremen with no trigge that looks like always holds and does not Given/When/Then What the example that proves the rule looks like Acceptance scenarios A rule with no concrete example, or an example with initial state Design by Contract What holds before, Rules, edges and assumptions An invariant nobody declared, and

after and always that the implementati breaks unnoticed ATDD How you know it is finished Success criteria and manual validation A criterion written after t code, that nev fails anything The common mistake is not picking the wrong notation. It is believing that one of them makes the others unnecessary. A spec with nothing but Given/When/Then scenarios says nothing about the cases nobody wrote a scenario for; a spec with nothing but EARS requirements has not a single example to check whether the rule was understood; a spec with both can still declare no invariant at all and leave the list free to reorder. What a Badly Formed Sentence Costs an Agent Close the chapter with the case the rest of the book will meet again in other clothes. Suppose the edit-task spec said only this about the title: FR-003 (hypothetical version): The edited title MUST remain non-repeated, on the same criterion as creation. The sentence looks complete. It has a subject, an obligation and a reference to a rule that exists in another document of the same project. It passes an inattentive reading, and it did: that was the original wording.

What it does not say is whether the task being edited counts in the comparison. A human reading that probably assumes it does not, because keeping your own title while fixing a description is obviously allowed. The agent assumed the opposite, compared against all tasks, and editing only the description started failing as a duplicate. The real text, after the defect showed up in manual validation, is this: FR-003: 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). The difference between the two versions is one word and one exclusion clause. The first inherited the rule by reference; the second states it. None of the four notations in this chapter would have stopped the first version from being written, but all four would have raised the question: EARS asks for the explicit condition, Given/When/Then asks for a scenario covering the case of keeping the title, Design by Contract asks what the operation's exact postcondition is, and ATDD would have put “edit only the description” on the validation list before any code existed. The fourth is the one that caught it. Chapter 6b comes back to this episode under another name and with another job. Here it served to show what an incomplete sentence costs. There it is the first of six patterns that repeat in specs of every kind, with the fix set beside each one. Before that, one question this chapter did not touch is still open: given that you know how to write a requirement sentence, how many of them fit in a single spec? That is the next chapter.

Footnotes “IEEE 830”, the Recommended Practice for Software Requirements Specifications standard (1984, 1993 and 1998 editions), superseded by ISO/IEC/IEEE 29148. Entry “Software requirements specification”, Wikipedia: https://en.wikipedia.org/wiki/Software_requirements_specification Alistair Mavin, Philip Wilkinson, Adrian Harwood and Mark Novak, “Easy Approach to Requirements Syntax (EARS)", 17th IEEE International Requirements Engineering Conference (RE'09), 2009, work developed in the context of aeronautical requirements at Rolls-Royce. The author's page, with a description of the patterns: https://alistairmavin.com/ears/ Dan North, “Introducing BDD” (originally published in Better Software, 2006), on the origin of Behaviour-Driven Development out of teaching TDD and on the Given/When/Then format: https://dannorth.net/introducing-bdd/ Bertrand Meyer, Design by Contract, formulated along with the Eiffel language in the second half of the 1980s, with preconditions, postconditions and class invariants. Entry “Design by contract”, Wikipedia: https://en.wikipedia.org/wiki/Design_by_contract “Acceptance test-driven development”, Wikipedia, on the practice of deriving acceptance tests from the customer's criteria before construction: https://en.wikipedia.org/wiki/Acceptance_test-driven_development

Powered by TurnKey Linux.