{"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":"fencing-the-change","title":"Fencing the change so it stays where you put it","url":"https://addaly.com/learn/building-apps-with-ai/fencing-the-change","position":17,"of":87,"minutes":8,"answer":"State which files may change and what must not be touched, ask for a file-by-file plan before code.","keyIdea":"State which files may change and what must not be touched, ask for a file-by-file plan before code, and check the result with git diff --stat because a fence is a filter rather than an enforcement."},"body":{"format":"markdown","text":"## The unrequested refactor\n\nYou ask for a cancel button. The diff shows changes in nine files. Somewhere in there the agent renamed a database column it thought was unclear, replaced your date handling with a library, and reorganised a folder.\n\nNone of it was malicious and some of it might even be improvements. It is still a problem, because you now cannot review the change you asked for (it is mixed in with four you did not), and if something breaks next week you have no idea which of the five caused it.\n\nThe fix is a fence, stated in the request.\n\n## What a fence looks like\n\n```\nAdd a Cancel button to BookingRow.tsx.\n\nChange only these files:\n  components/BookingRow.tsx\n  app/api/bookings/[id]/cancel/route.ts   (new)\n\nDo not modify the schema. Do not modify lib/queries.ts beyond\nadding one function. Do not add dependencies. Do not rename\nanything. Do not reformat files you touch.\n```\n\nSix lines of restriction for one line of request. That ratio is correct and it will feel excessive until the first time it saves you.\n\n\"Do not reformat\" earns its place on its own: a tool with different formatting settings can turn a three-line change into a 400-line diff, and a 400-line diff does not get read.\n\n## The standing fences\n\nSome restrictions belong in the rules file so you never type them again:\n\n```\n- Never change the database schema without asking first.\n- Never edit files in drizzle/migrations/. They are history.\n- No new dependencies without asking.\n- Do not refactor code I did not mention.\n- Do not change formatting in files you are editing for another reason.\n- Never edit .env or anything in .git.\n```\n\nThe migrations one matters more than it looks. A migration that has already run is a record of what happened to the database. Editing it does not change the database; it makes the record lie, and the next person to set up the project gets a different schema from yours.\n\n## Ask for the plan first\n\nFor anything non-trivial, one extra round trip:\n\n```\nBefore writing code: list the files you will change and one line\non what changes in each. Wait for me to confirm.\n```\n\nThe plan arrives in twenty seconds and is easy to evaluate. Half the time you will see a file in the list that has no business being there, and correcting it at that point costs nothing. Correcting it after the change means reading and reverting.\n\nThis is also the cheapest way to catch a misunderstanding. A plan that says \"modify the payment handler\" in response to a request about display formatting tells you the model has understood something else entirely, and it tells you before any code exists.\n\n## Fences do not enforce themselves\n\nA model can ignore a fence. It is text, not a permission system, and an agent convinced that a change requires touching another file will often touch it and mention this politely in its summary.\n\nSo the fence is a filter, not a guarantee, and the actual enforcement is `git diff --stat`:\n\n```bash\ngit diff --stat\n# components/BookingRow.tsx        | 24 ++++++++++\n# app/api/bookings/[id]/route.ts   | 41 +++++++++++++++\n# lib/dates.ts                     | 96 +++++++++++-------------\n```\n\nThat third line is the one to notice. You did not ask for `lib/dates.ts` and 96 lines changed in it. Ask why before you go any further, and be willing to say *revert that file and do the task without it*.\n\n## Fencing time as well as files\n\nThe same idea applies to how long a run goes. \"Do this one thing and stop\" is a fence. Without it, an agent that finishes early will look for adjacent work, and adjacent work is exactly the category you did not review.\n\nA useful closing line for any request: *when you are done, stop and summarise. Do not start anything else.*\n\n## When to take the fence down\n\nGreenfield work in a directory nobody depends on. A prototype you intend to throw away. Mechanical changes with a build or a test to catch mistakes. In those cases the fence costs more than it saves.\n\nThe rule of thumb: fence in proportion to how much working code is nearby. On an empty folder, let it run. In the file that handles payments, name every line it may touch."},"check":{"question":"Why is \"do not reformat files you are editing for another reason\" worth putting in a rules file?","options":[{"text":"Reformatting can introduce syntax errors that the build will not catch.","correct":false,"tempting":"Overstates the risk; formatters produce valid code, and the build would catch it if they did not."},{"text":"Consistent formatting across the project is a matter of team preference.","correct":false,"tempting":"Frames it as style politics, which is not why the rule pays for itself on a solo project."},{"text":"A reformat turns a three-line change into a several-hundred-line diff, and a diff that size stops being read.","correct":true},{"text":"Formatting changes make the git history larger and slow down the repository.","correct":false,"tempting":"Worries about storage, which is trivial, rather than about the review step that formatting noise destroys."}]},"prev":{"slug":"examples-beat-descriptions","title":"Examples beat descriptions","url":"https://addaly.com/learn/building-apps-with-ai/examples-beat-descriptions"},"next":{"slug":"questions-the-model-should-ask-you","title":"Making the model ask before it guesses","url":"https://addaly.com/learn/building-apps-with-ai/questions-the-model-should-ask-you"},"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) 'Fencing the change so it stays where you put it', in Building Apps with AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/building-apps-with-ai/fencing-the-change (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":"Fencing the change so it stays where you put it","courseTitle":"Building Apps with AI","module":{"index":2,"slug":"saying-what-you-want","title":"Saying what you want, precisely enough"},"answer":null,"keyIdea":"State which files may change and what must not be touched, ask for a file-by-file plan before code, and check the result with git diff --stat because a fence is a filter rather than an enforcement.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"the-unrequested-refactor","runs":[{"text":"The unrequested refactor"}]},{"t":"p","runs":[{"text":"You ask for a cancel button. The diff shows changes in nine files. Somewhere in there the agent renamed a database column it thought was unclear, replaced your date handling with a library, and reorganised a folder."}]},{"t":"p","runs":[{"text":"None of it was malicious and some of it might even be improvements. It is still a problem, because you now cannot review the change you asked for (it is mixed in with four you did not), and if something breaks next week you have no idea which of the five caused it."}]},{"t":"p","runs":[{"text":"The fix is a fence, stated in the request."}]},{"t":"h2","id":"what-a-fence-looks-like","runs":[{"text":"What a fence looks like"}]},{"t":"code","lang":"","label":"Text","text":"Add a Cancel button to BookingRow.tsx.\n\nChange only these files:\n  components/BookingRow.tsx\n  app/api/bookings/[id]/cancel/route.ts   (new)\n\nDo not modify the schema. Do not modify lib/queries.ts beyond\nadding one function. Do not add dependencies. Do not rename\nanything. Do not reformat files you touch."},{"t":"p","runs":[{"text":"Six lines of restriction for one line of request. That ratio is correct and it will feel excessive until the first time it saves you."}]},{"t":"p","runs":[{"text":"“Do not reformat” earns its place on its own: a tool with different formatting settings can turn a three-line change into a 400-line diff, and a 400-line diff does not get read."}]},{"t":"h2","id":"the-standing-fences","runs":[{"text":"The standing fences"}]},{"t":"p","runs":[{"text":"Some restrictions belong in the rules file so you never type them again:"}]},{"t":"code","lang":"","label":"Text","text":"- Never change the database schema without asking first.\n- Never edit files in drizzle/migrations/. They are history.\n- No new dependencies without asking.\n- Do not refactor code I did not mention.\n- Do not change formatting in files you are editing for another reason.\n- Never edit .env or anything in .git."},{"t":"p","runs":[{"text":"The migrations one matters more than it looks. A migration that has already run is a record of what happened to the database. Editing it does not change the database; it makes the record lie, and the next person to set up the project gets a different schema from yours."}]},{"t":"h2","id":"ask-for-the-plan-first","runs":[{"text":"Ask for the plan first"}]},{"t":"p","runs":[{"text":"For anything non-trivial, one extra round trip:"}]},{"t":"code","lang":"","label":"Text","text":"Before writing code: list the files you will change and one line\non what changes in each. Wait for me to confirm."},{"t":"p","runs":[{"text":"The plan arrives in twenty seconds and is easy to evaluate. Half the time you will see a file in the list that has no business being there, and correcting it at that point costs nothing. Correcting it after the change means reading and reverting."}]},{"t":"p","runs":[{"text":"This is also the cheapest way to catch a misunderstanding. A plan that says “modify the payment handler” in response to a request about display formatting tells you the model has understood something else entirely, and it tells you before any code exists."}]},{"t":"h2","id":"fences-do-not-enforce-themselves","runs":[{"text":"Fences do not enforce themselves"}]},{"t":"p","runs":[{"text":"A model can ignore a fence. It is text, not a permission system, and an agent convinced that a change requires touching another file will often touch it and mention this politely in its summary."}]},{"t":"p","runs":[{"text":"So the fence is a filter, not a guarantee, and the actual enforcement is "},{"text":"git diff --stat","code":true},{"text":":"}]},{"t":"code","lang":"bash","label":"Bash","text":"git diff --stat\n# components/BookingRow.tsx        | 24 ++++++++++\n# app/api/bookings/[id]/route.ts   | 41 +++++++++++++++\n# lib/dates.ts                     | 96 +++++++++++-------------"},{"t":"p","runs":[{"text":"That third line is the one to notice. You did not ask for "},{"text":"lib/dates.ts","code":true},{"text":" and 96 lines changed in it. Ask why before you go any further, and be willing to say "},{"text":"revert that file and do the task without it","i":true},{"text":"."}]},{"t":"h2","id":"fencing-time-as-well-as-files","runs":[{"text":"Fencing time as well as files"}]},{"t":"p","runs":[{"text":"The same idea applies to how long a run goes. “Do this one thing and stop” is a fence. Without it, an agent that finishes early will look for adjacent work, and adjacent work is exactly the category you did not review."}]},{"t":"p","runs":[{"text":"A useful closing line for any request: "},{"text":"when you are done, stop and summarise. Do not start anything else.","i":true}]},{"t":"h2","id":"when-to-take-the-fence-down","runs":[{"text":"When to take the fence down"}]},{"t":"p","runs":[{"text":"Greenfield work in a directory nobody depends on. A prototype you intend to throw away. Mechanical changes with a build or a test to catch mistakes. In those cases the fence costs more than it saves."}]},{"t":"p","runs":[{"text":"The rule of thumb: fence in proportion to how much working code is nearby. On an empty folder, let it run. In the file that handles payments, name every line it may touch."}]}],"check":{"question":[{"text":"Why is “do not reformat files you are editing for another reason” worth putting in a rules file?"}],"options":[{"text":[{"text":"Reformatting can introduce syntax errors that the build will not catch."}],"correct":false,"tempting":[{"text":"Overstates the risk; formatters produce valid code, and the build would catch it if they did not."}]},{"text":[{"text":"Consistent formatting across the project is a matter of team preference."}],"correct":false,"tempting":[{"text":"Frames it as style politics, which is not why the rule pays for itself on a solo project."}]},{"text":[{"text":"A reformat turns a three-line change into a several-hundred-line diff, and a diff that size stops being read."}],"correct":true,"tempting":null},{"text":[{"text":"Formatting changes make the git history larger and slow down the repository."}],"correct":false,"tempting":[{"text":"Worries about storage, which is trivial, rather than about the review step that formatting noise destroys."}]}],"order":[3,1,2,0],"why":null},"prev":{"slug":"examples-beat-descriptions","title":"Examples beat descriptions","minutes":8},"next":{"slug":"questions-the-model-should-ask-you","title":"Making the model ask before it guesses","minutes":8}}}