{"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":"was-it-actually-fixed","title":"Was it actually fixed, or just silenced","url":"https://addaly.com/learn/building-apps-with-ai/was-it-actually-fixed","position":34,"of":87,"minutes":8,"answer":"Undo the fix and confirm the bug returns; if it does not, the change you are about to keep is not what altered the behaviour.","keyIdea":"Undo the fix and confirm the bug returns; if it does not, the change you are about to keep is not what altered the behaviour."},"body":{"format":"markdown","text":"## Three ways an error can stop appearing\n\nOnly one of them is a fix.\n\n**The cause was removed.** The bad value is no longer produced. This is what you want.\n\n**The symptom was caught.** The failure still happens and is now swallowed by a `try/catch`, an optional chain, or a default value. The screen looks fine and the data is wrong.\n\n**The timing changed.** Nothing was fixed; the race is now usually won by the other side. It will come back on a slower connection, on a busier day, on somebody else's phone.\n\nAn agent reporting success cannot reliably tell you which of the three occurred, because from inside the code all three look like the error stopping.\n\n## The tells\n\nRead the fix diff for these shapes:\n\n```js\ntry { await save(booking) } catch (e) { console.error(e) }\n```\nThe save can fail and the user still sees success. Catching is correct only when something is done about it: a message, a retry, a rollback.\n\n```js\nconst name = booking?.customer?.name ?? 'Guest'\n```\nOptional chaining is fine when a missing customer is allowed. It is a disguise when the customer should always exist, because now a broken booking renders as *Guest* forever instead of telling you something is wrong.\n\n```js\nawait new Promise(r => setTimeout(r, 500))\n```\nA delay inserted to make something work is not synchronisation. It is a bet on timing that you will lose in production.\n\n```js\n// @ts-ignore\n```\nA type error silenced rather than resolved. Sometimes justified. Never without a comment saying why.\n\n## Two questions after any fix\n\nAsk them in these words:\n\n> Which line caused the error, and which line prevents it now?\n\nA real fix gives a specific answer to both. A vague answer (\"there was a state issue and this handles it more robustly\") is a strong signal that nobody knows what happened, and you should not accept it.\n\n> If the underlying condition still occurred, what would the user see?\n\nIf the answer is \"nothing\", the failure is now silent, which is worse than before. Errors that announce themselves are cheap. Errors that hide are the ones that reach a customer.\n\n## Prove it by making it fail\n\nThe strongest check available, and it takes a minute.\n\nUndo the fix. Confirm the bug returns. Reapply. Confirm it goes.\n\nIf the bug does *not* return when you remove the fix, the fix was not what changed the behaviour; something else did, and you are about to ship a change nobody understands and keep a bug you think is gone. This happens more often than people expect, particularly after several rounds of patching in one session.\n\n## Then check the neighbours\n\nA fix is a change, and a change can break something else. Before committing:\n\n- Run the acceptance checks you wrote for this feature.\n- Run the one adjacent feature that shares data with it.\n- Re-run whatever test suite exists, and read what it says rather than the summary line.\n\nThe pattern to watch for is a fix that makes the failing case pass by weakening a rule everything else depended on. Loosening an overlap check to admit a booking that should have been allowed can admit three that should not.\n\n## Turn it into a permanent check\n\nEvery bug that took more than twenty minutes deserves a written test, or at minimum a line in your acceptance checks file. You now have the reproduction and the expected result (the two hard parts) and writing it down costs a minute.\n\nBugs recur. Generated code recurs particularly, because the next agent to touch that file has not read this conversation and will happily restore the original mistake. A test is the only thing in this course that argues with a model on your behalf while you are asleep.\n\n## Commit the fix on its own\n\nOne commit, one fix, a message naming the bug. Not bundled with three other changes.\n\nWhen the same bug comes back in six weeks (and it will) `git log` for that file is a written record of what was tried and what it was supposed to do. That is the difference between fixing something twice and fixing it five times."},"check":{"question":"An agent fixes a crash by adding optional chaining so a missing customer renders as 'Guest'. When is that the wrong fix?","options":[{"text":"Whenever optional chaining is used, since it hides type errors.","correct":false,"tempting":"Rejects a normal language feature outright; the construct is correct wherever absence is genuinely expected."},{"text":"When the customer should always exist, because the display now conceals broken data indefinitely.","correct":true},{"text":"When the field is displayed to users rather than used internally.","correct":false,"tempting":"Draws the line at visibility, when the question is whether the missing value is legitimate."},{"text":"When there is no test covering that component.","correct":false,"tempting":"Names a separate gap; a fix is right or wrong regardless of what tests happen to exist."}]},"prev":{"slug":"it-builds-here-but-not-there","title":"It works here and not there","url":"https://addaly.com/learn/building-apps-with-ai/it-builds-here-but-not-there"},"next":{"slug":"restarting-the-conversation","title":"Knowing when to restart the conversation","url":"https://addaly.com/learn/building-apps-with-ai/restarting-the-conversation"},"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) 'Was it actually fixed, or just silenced', in Building Apps with AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/building-apps-with-ai/was-it-actually-fixed (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":"Was it actually fixed, or just silenced","courseTitle":"Building Apps with AI","module":{"index":4,"slug":"when-it-breaks","title":"When it breaks, and getting back"},"answer":null,"keyIdea":"Undo the fix and confirm the bug returns; if it does not, the change you are about to keep is not what altered the behaviour.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"three-ways-an-error-can-stop-appearing","runs":[{"text":"Three ways an error can stop appearing"}]},{"t":"p","runs":[{"text":"Only one of them is a fix."}]},{"t":"p","runs":[{"text":"The cause was removed.","b":true},{"text":" The bad value is no longer produced. This is what you want."}]},{"t":"p","runs":[{"text":"The symptom was caught.","b":true},{"text":" The failure still happens and is now swallowed by a "},{"text":"try/catch","code":true},{"text":", an optional chain, or a default value. The screen looks fine and the data is wrong."}]},{"t":"p","runs":[{"text":"The timing changed.","b":true},{"text":" Nothing was fixed; the race is now usually won by the other side. It will come back on a slower connection, on a busier day, on somebody else’s phone."}]},{"t":"p","runs":[{"text":"An agent reporting success cannot reliably tell you which of the three occurred, because from inside the code all three look like the error stopping."}]},{"t":"h2","id":"the-tells","runs":[{"text":"The tells"}]},{"t":"p","runs":[{"text":"Read the fix diff for these shapes:"}]},{"t":"code","lang":"js","label":"JavaScript","text":"try { await save(booking) } catch (e) { console.error(e) }"},{"t":"p","runs":[{"text":"The save can fail and the user still sees success. Catching is correct only when something is done about it: a message, a retry, a rollback."}]},{"t":"code","lang":"js","label":"JavaScript","text":"const name = booking?.customer?.name ?? 'Guest'"},{"t":"p","runs":[{"text":"Optional chaining is fine when a missing customer is allowed. It is a disguise when the customer should always exist, because now a broken booking renders as "},{"text":"Guest","i":true},{"text":" forever instead of telling you something is wrong."}]},{"t":"code","lang":"js","label":"JavaScript","text":"await new Promise(r => setTimeout(r, 500))"},{"t":"p","runs":[{"text":"A delay inserted to make something work is not synchronisation. It is a bet on timing that you will lose in production."}]},{"t":"code","lang":"js","label":"JavaScript","text":"// @ts-ignore"},{"t":"p","runs":[{"text":"A type error silenced rather than resolved. Sometimes justified. Never without a comment saying why."}]},{"t":"h2","id":"two-questions-after-any-fix","runs":[{"text":"Two questions after any fix"}]},{"t":"p","runs":[{"text":"Ask them in these words:"}]},{"t":"quote","runs":[{"text":"Which line caused the error, and which line prevents it now?"}]},{"t":"p","runs":[{"text":"A real fix gives a specific answer to both. A vague answer (“there was a state issue and this handles it more robustly”) is a strong signal that nobody knows what happened, and you should not accept it."}]},{"t":"quote","runs":[{"text":"If the underlying condition still occurred, what would the user see?"}]},{"t":"p","runs":[{"text":"If the answer is “nothing”, the failure is now silent, which is worse than before. Errors that announce themselves are cheap. Errors that hide are the ones that reach a customer."}]},{"t":"h2","id":"prove-it-by-making-it-fail","runs":[{"text":"Prove it by making it fail"}]},{"t":"p","runs":[{"text":"The strongest check available, and it takes a minute."}]},{"t":"p","runs":[{"text":"Undo the fix. Confirm the bug returns. Reapply. Confirm it goes."}]},{"t":"p","runs":[{"text":"If the bug does "},{"text":"not","i":true},{"text":" return when you remove the fix, the fix was not what changed the behaviour; something else did, and you are about to ship a change nobody understands and keep a bug you think is gone. This happens more often than people expect, particularly after several rounds of patching in one session."}]},{"t":"h2","id":"then-check-the-neighbours","runs":[{"text":"Then check the neighbours"}]},{"t":"p","runs":[{"text":"A fix is a change, and a change can break something else. Before committing:"}]},{"t":"ul","items":[[{"text":"Run the acceptance checks you wrote for this feature."}],[{"text":"Run the one adjacent feature that shares data with it."}],[{"text":"Re-run whatever test suite exists, and read what it says rather than the summary line."}]]},{"t":"p","runs":[{"text":"The pattern to watch for is a fix that makes the failing case pass by weakening a rule everything else depended on. Loosening an overlap check to admit a booking that should have been allowed can admit three that should not."}]},{"t":"h2","id":"turn-it-into-a-permanent-check","runs":[{"text":"Turn it into a permanent check"}]},{"t":"p","runs":[{"text":"Every bug that took more than twenty minutes deserves a written test, or at minimum a line in your acceptance checks file. You now have the reproduction and the expected result (the two hard parts) and writing it down costs a minute."}]},{"t":"p","runs":[{"text":"Bugs recur. Generated code recurs particularly, because the next agent to touch that file has not read this conversation and will happily restore the original mistake. A test is the only thing in this course that argues with a model on your behalf while you are asleep."}]},{"t":"h2","id":"commit-the-fix-on-its-own","runs":[{"text":"Commit the fix on its own"}]},{"t":"p","runs":[{"text":"One commit, one fix, a message naming the bug. Not bundled with three other changes."}]},{"t":"p","runs":[{"text":"When the same bug comes back in six weeks (and it will) "},{"text":"git log","code":true},{"text":" for that file is a written record of what was tried and what it was supposed to do. That is the difference between fixing something twice and fixing it five times."}]}],"check":{"question":[{"text":"An agent fixes a crash by adding optional chaining so a missing customer renders as ‘Guest’. When is that the wrong fix?"}],"options":[{"text":[{"text":"Whenever optional chaining is used, since it hides type errors."}],"correct":false,"tempting":[{"text":"Rejects a normal language feature outright; the construct is correct wherever absence is genuinely expected."}]},{"text":[{"text":"When the customer should always exist, because the display now conceals broken data indefinitely."}],"correct":true,"tempting":null},{"text":[{"text":"When the field is displayed to users rather than used internally."}],"correct":false,"tempting":[{"text":"Draws the line at visibility, when the question is whether the missing value is legitimate."}]},{"text":[{"text":"When there is no test covering that component."}],"correct":false,"tempting":[{"text":"Names a separate gap; a fix is right or wrong regardless of what tests happen to exist."}]}],"order":[0,1,3,2],"why":null},"prev":{"slug":"it-builds-here-but-not-there","title":"It works here and not there","minutes":8},"next":{"slug":"restarting-the-conversation","title":"Knowing when to restart the conversation","minutes":8}}}