{"course":{"slug":"building-apps-with-ai","title":"Building Apps with AI","url":"https://addaly.com/learn/building-apps-with-ai","level":"beginner","track":"build"},"lesson":{"slug":"acceptance-checks-written-first","title":"Writing the acceptance checks before the code","url":"https://addaly.com/learn/building-apps-with-ai/acceptance-checks-written-first","position":13,"of":87,"minutes":8,"answer":"Write five given-when-then checks before the code (happy path, boundary, refusal, permission and recovery) because a check written afterwards only describes what the code already does.","keyIdea":"Write five given-when-then checks before the code (happy path, boundary, refusal, permission and recovery) because a check written afterwards only describes what the code already does."},"body":{"format":"markdown","text":"## The gap between it works and it does what I meant\n\nAn agent finishes and reports success. What was verified? That the code compiles, usually, and sometimes that a test it wrote passes. Nothing checked that the feature does the thing in your head, because the thing in your head was never written down in a form that could fail.\n\nAn acceptance check is that form. It is one sentence with three parts, in ordinary words, written **before** the work.\n\n> **Given** a booking at 14:00 for Anita lasting 30 minutes,\n> **when** a customer tries to book Anita at 14:15,\n> **then** the booking is refused and the message names the next three free slots.\n\nYou can hand that to a model, a tester, or yourself in a month, and all three will check the same thing.\n\n## Why before\n\nWritten afterwards, a check describes what the code does. That is a description, not a test: it will pass by construction and tell you nothing.\n\nWritten first, it is a commitment. If the code does something else, one of you is wrong, and finding out which is the whole point.\n\nThis is the same discipline as writing down what result would make you ship before you run an experiment. The order is what makes it honest.\n\n## Five checks per feature, not fifty\n\nMore is not better. For each feature, write:\n\n- **One happy path.** The thing working normally.\n- **One boundary.** The edge of a rule. A booking that starts exactly when another ends. A cart of exactly zero items. A name of exactly 100 characters.\n- **One refusal.** The thing that must not be allowed, and the message the person sees.\n- **One permission.** Somebody else's data, attempted by the wrong person. Every feature that touches user data gets this one.\n- **One recovery.** What happens when the network drops mid-save, or the user presses back, or submits twice.\n\nThose five catch the great majority of what actually goes wrong in generated code, and they take about ten minutes to write.\n\n## The double-submit check, specifically\n\nWrite this one down for every form that costs money or creates something:\n\n> **Given** a customer on the booking form, **when** they press Book twice quickly, **then** exactly one booking exists.\n\nGenerated forms fail this constantly. The button stays enabled, the request fires twice, and you have two identical bookings, two charges, or two emails. It is not an exotic edge case: impatient people on slow connections do it every day.\n\n## Turning checks into tests, later\n\nEach of these can eventually become an automated test, and the good news is that the hard part is already done. A model given a written acceptance check writes a decent test from it, because the check already names the setup, the action and the expected result.\n\nDo not automate them all on day one. Write them as sentences, check them by hand, and convert the ones you are tired of repeating. The testing lesson later in this course covers which five to automate first.\n\n## Hand them over with the request\n\n```\nBuild the overlap rule. It must satisfy:\n\n1. 14:00 booking, 30 min. New booking at 14:15 for the same\n   stylist: refused.\n2. 14:00 booking, 30 min. New booking at 14:30 for the same\n   stylist: allowed. Back to back is fine.\n3. 14:15 for a different stylist: allowed.\n4. Refusal message lists the next three free slots that day.\n5. Two identical requests sent at once create one booking.\n\nAfter you finish, tell me how you verified each of the five.\n```\n\nThat last sentence is worth including every time. It converts \"done\" into a claim about evidence, and a vague answer to it is itself informative: if the model cannot say how it verified check 5, then check 5 is not done.\n\n## The check that never passes\n\nSometimes you write a check and discover you cannot say what the right answer is. Two customers, same slot, requests one millisecond apart. Which one wins?\n\nThat is not a failure of the method. That is the method working: it found a decision nobody had made. Make it, write it down, and continue. Finding those before the code exists is most of what this practice buys you."},"check":{"question":"What makes an acceptance check written before the work more useful than one written after?","options":[{"text":"It is more likely to be phrased in language the model can act on.","correct":false,"tempting":"Treats a wording advantage as the point; the same sentence written later reads identically and still proves nothing."},{"text":"Written first it is a commitment the code can fail, whereas written afterwards it describes behaviour that already exists.","correct":true},{"text":"It can be converted into an automated test more easily.","correct":false,"tempting":"Both convert equally well; the timing question is about honesty of the result rather than convenience of automation."},{"text":"It forces the developer to think about the data model earlier.","correct":false,"tempting":"Names a side benefit that also arrives with a later check, and misses the reason the ordering changes what the check is worth."}]},"prev":{"slug":"slicing-a-feature-into-checkable-steps","title":"Slicing a feature so every step is checkable","url":"https://addaly.com/learn/building-apps-with-ai/slicing-a-feature-into-checkable-steps"},"next":{"slug":"sketching-the-screens","title":"Sketching the screens, including the empty ones","url":"https://addaly.com/learn/building-apps-with-ai/sketching-the-screens"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Writing the acceptance checks before the code', in Building Apps with AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/building-apps-with-ai/acceptance-checks-written-first (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Writing the acceptance checks before the code","courseTitle":"Building Apps with AI","module":{"index":2,"slug":"saying-what-you-want","title":"Saying what you want, precisely enough"},"answer":null,"keyIdea":"Write five given-when-then checks before the code (happy path, boundary, refusal, permission and recovery) because a check written afterwards only describes what the code already does.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"the-gap-between-it-works-and-it-does-what-i-meant","runs":[{"text":"The gap between it works and it does what I meant"}]},{"t":"p","runs":[{"text":"An agent finishes and reports success. What was verified? That the code compiles, usually, and sometimes that a test it wrote passes. Nothing checked that the feature does the thing in your head, because the thing in your head was never written down in a form that could fail."}]},{"t":"p","runs":[{"text":"An acceptance check is that form. It is one sentence with three parts, in ordinary words, written "},{"text":"before","b":true},{"text":" the work."}]},{"t":"quote","runs":[{"text":"Given","b":true},{"text":" a booking at 14:00 for Anita lasting 30 minutes,"}]},{"t":"quote","runs":[{"text":"when","b":true},{"text":" a customer tries to book Anita at 14:15,"}]},{"t":"quote","runs":[{"text":"then","b":true},{"text":" the booking is refused and the message names the next three free slots."}]},{"t":"p","runs":[{"text":"You can hand that to a model, a tester, or yourself in a month, and all three will check the same thing."}]},{"t":"h2","id":"why-before","runs":[{"text":"Why before"}]},{"t":"p","runs":[{"text":"Written afterwards, a check describes what the code does. That is a description, not a test: it will pass by construction and tell you nothing."}]},{"t":"p","runs":[{"text":"Written first, it is a commitment. If the code does something else, one of you is wrong, and finding out which is the whole point."}]},{"t":"p","runs":[{"text":"This is the same discipline as writing down what result would make you ship before you run an experiment. The order is what makes it honest."}]},{"t":"h2","id":"five-checks-per-feature-not-fifty","runs":[{"text":"Five checks per feature, not fifty"}]},{"t":"p","runs":[{"text":"More is not better. For each feature, write:"}]},{"t":"ul","items":[[{"text":"One happy path.","b":true},{"text":" The thing working normally."}],[{"text":"One boundary.","b":true},{"text":" The edge of a rule. A booking that starts exactly when another ends. A cart of exactly zero items. A name of exactly 100 characters."}],[{"text":"One refusal.","b":true},{"text":" The thing that must not be allowed, and the message the person sees."}],[{"text":"One permission.","b":true},{"text":" Somebody else’s data, attempted by the wrong person. Every feature that touches user data gets this one."}],[{"text":"One recovery.","b":true},{"text":" What happens when the network drops mid-save, or the user presses back, or submits twice."}]]},{"t":"p","runs":[{"text":"Those five catch the great majority of what actually goes wrong in generated code, and they take about ten minutes to write."}]},{"t":"h2","id":"the-double-submit-check-specifically","runs":[{"text":"The double-submit check, specifically"}]},{"t":"p","runs":[{"text":"Write this one down for every form that costs money or creates something:"}]},{"t":"quote","runs":[{"text":"Given","b":true},{"text":" a customer on the booking form, "},{"text":"when","b":true},{"text":" they press Book twice quickly, "},{"text":"then","b":true},{"text":" exactly one booking exists."}]},{"t":"p","runs":[{"text":"Generated forms fail this constantly. The button stays enabled, the request fires twice, and you have two identical bookings, two charges, or two emails. It is not an exotic edge case: impatient people on slow connections do it every day."}]},{"t":"h2","id":"turning-checks-into-tests-later","runs":[{"text":"Turning checks into tests, later"}]},{"t":"p","runs":[{"text":"Each of these can eventually become an automated test, and the good news is that the hard part is already done. A model given a written acceptance check writes a decent test from it, because the check already names the setup, the action and the expected result."}]},{"t":"p","runs":[{"text":"Do not automate them all on day one. Write them as sentences, check them by hand, and convert the ones you are tired of repeating. The testing lesson later in this course covers which five to automate first."}]},{"t":"h2","id":"hand-them-over-with-the-request","runs":[{"text":"Hand them over with the request"}]},{"t":"code","lang":"","label":"Text","text":"Build the overlap rule. It must satisfy:\n\n1. 14:00 booking, 30 min. New booking at 14:15 for the same\n   stylist: refused.\n2. 14:00 booking, 30 min. New booking at 14:30 for the same\n   stylist: allowed. Back to back is fine.\n3. 14:15 for a different stylist: allowed.\n4. Refusal message lists the next three free slots that day.\n5. Two identical requests sent at once create one booking.\n\nAfter you finish, tell me how you verified each of the five."},{"t":"p","runs":[{"text":"That last sentence is worth including every time. It converts “done” into a claim about evidence, and a vague answer to it is itself informative: if the model cannot say how it verified check 5, then check 5 is not done."}]},{"t":"h2","id":"the-check-that-never-passes","runs":[{"text":"The check that never passes"}]},{"t":"p","runs":[{"text":"Sometimes you write a check and discover you cannot say what the right answer is. Two customers, same slot, requests one millisecond apart. Which one wins?"}]},{"t":"p","runs":[{"text":"That is not a failure of the method. That is the method working: it found a decision nobody had made. Make it, write it down, and continue. Finding those before the code exists is most of what this practice buys you."}]}],"check":{"question":[{"text":"What makes an acceptance check written before the work more useful than one written after?"}],"options":[{"text":[{"text":"It is more likely to be phrased in language the model can act on."}],"correct":false,"tempting":[{"text":"Treats a wording advantage as the point; the same sentence written later reads identically and still proves nothing."}]},{"text":[{"text":"Written first it is a commitment the code can fail, whereas written afterwards it describes behaviour that already exists."}],"correct":true,"tempting":null},{"text":[{"text":"It can be converted into an automated test more easily."}],"correct":false,"tempting":[{"text":"Both convert equally well; the timing question is about honesty of the result rather than convenience of automation."}]},{"text":[{"text":"It forces the developer to think about the data model earlier."}],"correct":false,"tempting":[{"text":"Names a side benefit that also arrives with a later check, and misses the reason the ordering changes what the check is worth."}]}],"order":[2,3,0,1],"why":null},"prev":{"slug":"slicing-a-feature-into-checkable-steps","title":"Slicing a feature so every step is checkable","minutes":8},"next":{"slug":"sketching-the-screens","title":"Sketching the screens, including the empty ones","minutes":8}}}