{"course":{"slug":"how-software-changed","title":"How Software Development Changed","url":"https://addaly.com/learn/how-software-changed","level":"intermediate","track":"work"},"lesson":{"slug":"signs-the-loop-has-gone-bad","title":"Signs the loop has gone bad","url":"https://addaly.com/learn/how-software-changed/signs-the-loop-has-gone-bad","position":49,"of":87,"minutes":8,"answer":"The dangerous session is the one that feels productive for two hours, so watch for the diff growing unread, nothing being run, tests moving to match code, and suppressions appearing.","keyIdea":"The dangerous session is the one that feels productive for two hours, so watch for the diff growing unread, nothing being run, tests moving to match code, and suppressions appearing, and go back to the last commit that worked rather than pushing on."},"body":{"format":"markdown","text":"## A session can fail slowly\n\nThe failures worth catching are not the dramatic ones. They are the sessions that feel productive for two hours and produce a change nobody should merge. Here are the signals, roughly in the order they appear.\n\n> Figure: A session that fails slowly. Timeline: Early signals, The diff is growing and you have stopped reading it. Nothing has been run for twenty minutes. You are explaining rather than specifying.; So: ten minutes, Stop, run something, read the diff, commit what works and discard the rest.; Middle signals, Tests are moving to match the code. Dependencies you did not ask for. Suppressions appearing. Eleven files touched for a date format.; So: go back, Return to the last commit that worked. There is nothing to lose, because the code either works and is understood or it does not.; Late signals, You cannot describe the change in one sentence. You are hoping the tests are right rather than knowing. You would not defend line 47 in review.; So: put it down, Tomorrow, or somebody else, or by hand in a quarter of the time now that you know what it involves.. The dangerous session is not the dramatic one. It is the one that feels productive for two hours and produces a change nobody should merge.\n\n## The early signals\n\n**The diff is growing and you have stopped reading it.** The most reliable indicator of all. If you cannot say roughly how many lines have changed and which files, you are no longer working: you are watching.\n\n**You have not run anything for twenty minutes.** Steps that end in something executable are what keep the cost of being wrong bounded. A long stretch with no execution means the error, when you find it, could be anywhere in the whole stretch.\n\n**You are explaining rather than specifying.** Long paragraphs of justification, restating what you already said in different words. That is arguing, and arguing means restart.\n\n**You have accepted something you did not understand** and told yourself you would come back to it. You will not, and it is now load-bearing.\n\n## The middle signals\n\n**Tests are being changed to match code.** Watch for this specifically in the diff. A test whose assertion moved to accommodate a new implementation is either a genuine correction, in which case somebody should say so out loud, with a reason, or it is the bug being ratified.\n\n**New dependencies you did not ask for.** Check `package.json`, `requirements.txt`, `go.mod`, `Cargo.toml` in every session's diff. Something got added to solve a problem you could have solved with nine lines, and it is now part of your supply chain.\n\n**The file count is drifting past what the task needs.** A change to date formatting that touches eleven files is doing something else as well.\n\n**Suppressions are appearing.** `# type: ignore`, `eslint-disable`, `@SuppressWarnings`, `any`, a widened exception. Each of these is a check being switched off to make progress.\n\n## The late signals\n\n**You cannot describe the change in one sentence.** If \"what does this pull request do\" needs a paragraph with an \"and also\" in it, the change is several changes and none of them has been reviewed.\n\n**You are hoping the tests are right rather than knowing.** The moment you notice yourself relieved that CI is green rather than confirming it, the suite has become a wish.\n\n**You would not defend this in review.** The strongest signal and the easiest to check. Imagine a colleague asking why line 47 is there. If the honest answer is a shrug, the work is not finished, regardless of whether it functions.\n\n## What to do at each stage\n\n**Early:** stop, run something, read the diff, commit what works and discard the rest. Ten minutes.\n\n**Middle:** `git stash` or `git checkout .`, and go back to the last commit that worked. This is the moment where people push on because so much has been done. There is nothing to lose: the code either works and is understood, or it does not, and no amount of it is worth keeping in the second case.\n\n**Late:** put it down entirely. Come back tomorrow, or hand it to somebody else, or do it by hand in a quarter of the time now that you know what it involves.\n\n## The two-line log that makes this concrete\n\nAt the end of any session longer than an hour, write two lines in a scratch file:\n\n```\n2026-09-08  fx conversion.  3 restarts.  ~90 min.  Merged.\n            Restart 2 was the real one — it kept using the rate table\n            directly because I never attached rate_provider.py.\n```\n\nAfter a month you will have a list of your own failure patterns, and they will be more specific than anything in this course. Most people find the same two or three recur: a file that was never attached, a constraint never stated, a task too large to have been asked as one thing.\n\nThose are fixable, and they are only fixable if you can see them, which requires writing them down at the moment they happen rather than reconstructing them later, which, as the first module established, you cannot do accurately.\n\n## One more signal, from the other side of the desk\n\nIf you manage people, the equivalent signal is a pull request whose author cannot answer a question about it within a few seconds. Not a hard question: any question. That gap between submitting and understanding is the whole failure mode of this era, and it is visible for free in a two-minute conversation, long before it is visible in any metric you could put on a dashboard."},"check":{"question":"Two hours into a session, a developer notices the diff spans nineteen files, two assertions have been adjusted, and a `# type: ignore` has appeared. They are 90% done. What is the reasoning for discarding the work?","options":[{"text":"Nineteen files exceeds any reasonable review size, so the change would be rejected anyway and the time is better spent splitting it.","correct":false,"tempting":"Diff size is a real blocker, but splitting a change nobody understands produces several changes nobody understands."},{"text":"The adjusted assertions and the suppression are individually small, so the correct response is to revert those four lines and keep the rest.","correct":false,"tempting":"Treats the symptoms as the problem; they are evidence that the whole session drifted, and the rest carries the same drift unlabelled."},{"text":"The three signals together indicate a change nobody understands, and partial work of that kind has no value to preserve.","correct":true},{"text":"Two hours without a commit means git cannot help recover intermediate states, so the only clean position is the last known-good one.","correct":false,"tempting":"Correct about the recovery position, but it makes the case about tooling when the reason to discard is what the signals reveal about the work."}]},"prev":{"slug":"a-loop-that-costs-nothing","title":"A complete free workflow","url":"https://addaly.com/learn/how-software-changed/a-loop-that-costs-nothing"},"next":{"slug":"what-got-harder","title":"The parts that got harder","url":"https://addaly.com/learn/how-software-changed/what-got-harder"},"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) 'Signs the loop has gone bad', in How Software Development Changed. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/how-software-changed/signs-the-loop-has-gone-bad (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":"Signs the loop has gone bad","courseTitle":"How Software Development Changed","module":{"index":5,"slug":"the-working-loop","title":"The working loop"},"answer":null,"keyIdea":"The dangerous session is the one that feels productive for two hours, so watch for the diff growing unread, nothing being run, tests moving to match code, and suppressions appearing, and go back to the last commit that worked rather than pushing on.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"timeline","title":"A session that fails slowly","events":[{"when":"Early signals","what":"The diff is growing and you have stopped reading it. Nothing has been run for twenty minutes. You are explaining rather than specifying."},{"when":"So: ten minutes","what":"Stop, run something, read the diff, commit what works and discard the rest."},{"when":"Middle signals","what":"Tests are moving to match the code. Dependencies you did not ask for. Suppressions appearing. Eleven files touched for a date format."},{"when":"So: go back","what":"Return to the last commit that worked. There is nothing to lose, because the code either works and is understood or it does not."},{"when":"Late signals","what":"You cannot describe the change in one sentence. You are hoping the tests are right rather than knowing. You would not defend line 47 in review."},{"when":"So: put it down","what":"Tomorrow, or somebody else, or by hand in a quarter of the time now that you know what it involves."}],"caption":"The dangerous session is not the dramatic one. It is the one that feels productive for two hours and produces a change nobody should merge."},"alt":"Figure: A session that fails slowly. Timeline: Early signals, The diff is growing and you have stopped reading it. Nothing has been run for twenty minutes. You are explaining rather than specifying.; So: ten minutes, Stop, run something, read the diff, commit what works and discard the rest.; Middle signals, Tests are moving to match the code. Dependencies you did not ask for. Suppressions appearing. Eleven files touched for a date format.; So: go back, Return to the last commit that worked. There is nothing to lose, because the code either works and is understood or it does not.; Late signals, You cannot describe the change in one sentence. You are hoping the tests are right rather than knowing. You would not defend line 47 in review.; So: put it down, Tomorrow, or somebody else, or by hand in a quarter of the time now that you know what it involves.. The dangerous session is not the dramatic one. It is the one that feels productive for two hours and produces a change nobody should merge."}},"blocks":[{"t":"h2","id":"a-session-can-fail-slowly","runs":[{"text":"A session can fail slowly"}]},{"t":"p","runs":[{"text":"The failures worth catching are not the dramatic ones. They are the sessions that feel productive for two hours and produce a change nobody should merge. Here are the signals, roughly in the order they appear."}]},{"t":"h2","id":"the-early-signals","runs":[{"text":"The early signals"}]},{"t":"p","runs":[{"text":"The diff is growing and you have stopped reading it.","b":true},{"text":" The most reliable indicator of all. If you cannot say roughly how many lines have changed and which files, you are no longer working: you are watching."}]},{"t":"p","runs":[{"text":"You have not run anything for twenty minutes.","b":true},{"text":" Steps that end in something executable are what keep the cost of being wrong bounded. A long stretch with no execution means the error, when you find it, could be anywhere in the whole stretch."}]},{"t":"p","runs":[{"text":"You are explaining rather than specifying.","b":true},{"text":" Long paragraphs of justification, restating what you already said in different words. That is arguing, and arguing means restart."}]},{"t":"p","runs":[{"text":"You have accepted something you did not understand","b":true},{"text":" and told yourself you would come back to it. You will not, and it is now load-bearing."}]},{"t":"h2","id":"the-middle-signals","runs":[{"text":"The middle signals"}]},{"t":"p","runs":[{"text":"Tests are being changed to match code.","b":true},{"text":" Watch for this specifically in the diff. A test whose assertion moved to accommodate a new implementation is either a genuine correction, in which case somebody should say so out loud, with a reason, or it is the bug being ratified."}]},{"t":"p","runs":[{"text":"New dependencies you did not ask for.","b":true},{"text":" Check "},{"text":"package.json","code":true},{"text":", "},{"text":"requirements.txt","code":true},{"text":", "},{"text":"go.mod","code":true},{"text":", "},{"text":"Cargo.toml","code":true},{"text":" in every session’s diff. Something got added to solve a problem you could have solved with nine lines, and it is now part of your supply chain."}]},{"t":"p","runs":[{"text":"The file count is drifting past what the task needs.","b":true},{"text":" A change to date formatting that touches eleven files is doing something else as well."}]},{"t":"p","runs":[{"text":"Suppressions are appearing.","b":true},{"text":" "},{"text":"# type: ignore","code":true},{"text":", "},{"text":"eslint-disable","code":true},{"text":", "},{"text":"@SuppressWarnings","code":true},{"text":", "},{"text":"any","code":true},{"text":", a widened exception. Each of these is a check being switched off to make progress."}]},{"t":"h2","id":"the-late-signals","runs":[{"text":"The late signals"}]},{"t":"p","runs":[{"text":"You cannot describe the change in one sentence.","b":true},{"text":" If “what does this pull request do” needs a paragraph with an “and also” in it, the change is several changes and none of them has been reviewed."}]},{"t":"p","runs":[{"text":"You are hoping the tests are right rather than knowing.","b":true},{"text":" The moment you notice yourself relieved that CI is green rather than confirming it, the suite has become a wish."}]},{"t":"p","runs":[{"text":"You would not defend this in review.","b":true},{"text":" The strongest signal and the easiest to check. Imagine a colleague asking why line 47 is there. If the honest answer is a shrug, the work is not finished, regardless of whether it functions."}]},{"t":"h2","id":"what-to-do-at-each-stage","runs":[{"text":"What to do at each stage"}]},{"t":"p","runs":[{"text":"Early:","b":true},{"text":" stop, run something, read the diff, commit what works and discard the rest. Ten minutes."}]},{"t":"p","runs":[{"text":"Middle:","b":true},{"text":" "},{"text":"git stash","code":true},{"text":" or "},{"text":"git checkout .","code":true},{"text":", and go back to the last commit that worked. This is the moment where people push on because so much has been done. There is nothing to lose: the code either works and is understood, or it does not, and no amount of it is worth keeping in the second case."}]},{"t":"p","runs":[{"text":"Late:","b":true},{"text":" put it down entirely. Come back tomorrow, or hand it to somebody else, or do it by hand in a quarter of the time now that you know what it involves."}]},{"t":"h2","id":"the-two-line-log-that-makes-this-concrete","runs":[{"text":"The two-line log that makes this concrete"}]},{"t":"p","runs":[{"text":"At the end of any session longer than an hour, write two lines in a scratch file:"}]},{"t":"code","lang":"","label":"Text","text":"2026-09-08  fx conversion.  3 restarts.  ~90 min.  Merged.\n            Restart 2 was the real one — it kept using the rate table\n            directly because I never attached rate_provider.py."},{"t":"p","runs":[{"text":"After a month you will have a list of your own failure patterns, and they will be more specific than anything in this course. Most people find the same two or three recur: a file that was never attached, a constraint never stated, a task too large to have been asked as one thing."}]},{"t":"p","runs":[{"text":"Those are fixable, and they are only fixable if you can see them, which requires writing them down at the moment they happen rather than reconstructing them later, which, as the first module established, you cannot do accurately."}]},{"t":"h2","id":"one-more-signal-from-the-other-side-of-the-desk","runs":[{"text":"One more signal, from the other side of the desk"}]},{"t":"p","runs":[{"text":"If you manage people, the equivalent signal is a pull request whose author cannot answer a question about it within a few seconds. Not a hard question: any question. That gap between submitting and understanding is the whole failure mode of this era, and it is visible for free in a two-minute conversation, long before it is visible in any metric you could put on a dashboard."}]}],"check":{"question":[{"text":"Two hours into a session, a developer notices the diff spans nineteen files, two assertions have been adjusted, and a "},{"text":"# type: ignore","code":true},{"text":" has appeared. They are 90% done. What is the reasoning for discarding the work?"}],"options":[{"text":[{"text":"Nineteen files exceeds any reasonable review size, so the change would be rejected anyway and the time is better spent splitting it."}],"correct":false,"tempting":[{"text":"Diff size is a real blocker, but splitting a change nobody understands produces several changes nobody understands."}]},{"text":[{"text":"The adjusted assertions and the suppression are individually small, so the correct response is to revert those four lines and keep the rest."}],"correct":false,"tempting":[{"text":"Treats the symptoms as the problem; they are evidence that the whole session drifted, and the rest carries the same drift unlabelled."}]},{"text":[{"text":"The three signals together indicate a change nobody understands, and partial work of that kind has no value to preserve."}],"correct":true,"tempting":null},{"text":[{"text":"Two hours without a commit means git cannot help recover intermediate states, so the only clean position is the last known-good one."}],"correct":false,"tempting":[{"text":"Correct about the recovery position, but it makes the case about tooling when the reason to discard is what the signals reveal about the work."}]}],"order":[1,0,3,2],"why":null},"prev":{"slug":"a-loop-that-costs-nothing","title":"A complete free workflow","minutes":9},"next":{"slug":"what-got-harder","title":"The parts that got harder","minutes":10}}}