{"course":{"slug":"how-software-changed","title":"How Software Development Changed","url":"https://addaly.com/learn/how-software-changed","level":"intermediate","track":"work"},"lesson":{"slug":"consolidation-and-entropy","title":"Scheduling the deletion","url":"https://addaly.com/learn/how-software-changed/consolidation-and-entropy","position":55,"of":87,"minutes":9,"answer":"Adding code is easy and locally justified while deleting requires understanding the whole system, so consolidation must be scheduled rather than hoped for.","keyIdea":"Adding code is easy and locally justified while deleting requires understanding the whole system, so consolidation must be scheduled rather than hoped for, and the argument that works with management is incident arithmetic rather than elegance."},"body":{"format":"markdown","text":"## Why codebases now grow faster than they improve\n\nIt is cheaper to generate a new 200-line helper than to find the existing one, read it, and decide whether it fits. So duplication rises, and nothing in the process pushes back.\n\nThe asymmetry is structural rather than cultural. Adding is easy and locally justified: the new code works, the tests pass, the reviewer has no reason to object. Deleting requires knowing that nothing depends on it, which requires understanding the whole system, which is the expensive thing. So the default direction is accumulation, and consolidation only happens if somebody schedules it.\n\nPreviously there was an accidental brake: writing code was slow enough that people reused things out of laziness. That brake is gone. Nothing replaced it.\n\n## Find the duplication mechanically\n\nYou do not have to read the codebase to know where the copies are.\n\n```bash\n# copy-paste detection across most languages, free\nnpx jscpd src/ --min-lines 8 --min-tokens 60 --reporters console\n\n# the cheap version: which function names exist several times?\nrg -o '^\\s*(def|function|func) (\\w+)' -r '$2' --no-filename \\\n  | sort | uniq -c | sort -rn | head -30\n```\n\nThat second command takes four seconds and routinely finds three date formatters, two retry helpers and four ways of building the same URL. Run it on your own repository before reading further; the result is usually the most persuasive argument in this lesson.\n\n## Find the dead code mechanically\n\n```bash\nvulture src/                 # Python\nnpx ts-prune                 # TypeScript unused exports\nnpx knip                     # unused files, deps and exports\ndeadcode ./...               # Go\ncargo +nightly udeps         # unused Rust dependencies\n```\n\nThese produce false positives (anything called by reflection, by a framework's convention, or from a configuration string) so treat the output as a list to check, not a list to delete. Even so, a first run on a codebase that has never had one usually finds several per cent of the files.\n\nThe strongest version, if you can do it: coverage instrumentation in production for a week. Code that never executes under real traffic is a much more reliable signal than static analysis, and it catches the endpoint that no longer has any callers.\n\n## Make it a scheduled event\n\nConsolidation loses every fair fight against a feature, so do not have the fight. Book the time.\n\nThe blunt version that works: **one day a month, everybody, deletion only.** No new features. The rules make it work:\n\n- Deleting code counts. Deleting a dependency counts double.\n- Merging three implementations into one counts.\n- Removing a stale feature flag counts.\n- Adding anything does not count, unless it is a test that lets you delete something.\n\nMeasure it and publish the number, because it is the one day where lines-of-code-removed is a legitimate metric. A team that removes 4,000 lines and two dependencies in a day has done more for next year's velocity than a normal feature day, and saying so out loud is what makes people take the next one seriously.\n\n## The argument to management\n\nDo not argue about elegance. Argue about the numbers you are already tracking.\n\n> Four date formatters means a date bug has to be fixed four times, and last quarter we fixed it twice and missed two. Every duplicate is a permanent multiplier on the cost of every future change in that area, and on the probability that a fix is incomplete.\n>\n> Deleting the abandoned reporting module removes 6,000 lines, two dependencies with open advisories, and one database table we back up nightly. It reduces our attack surface and our review surface, and it takes one day.\n\nSecurity and incident arithmetic persuade where aesthetics do not, and both are true.\n\n## The habit that prevents most of it\n\nOne norm, in the contributing guide:\n\n> Before adding a helper, search for one. If you find something close, either use it or write one sentence in the pull request saying why it did not fit.\n\nThat sentence is the whole control. It costs the author thirty seconds, it gives the reviewer something specific to check, and it is the only point in the process where the duplication decision is visible to anybody.\n\nAnd the corresponding norm for the other direction: **every feature flag gets a removal date when it is created, and the removal ticket is created at the same time.** A codebase with two hundred stale flags has a configuration space nobody can reason about, and each individual flag was obviously fine to leave."},"check":{"question":"A team runs a duplication detector and finds four near-identical retry helpers, one of which retries non-idempotent POST requests. What is the strongest argument for consolidating them?","options":[{"text":"Four implementations means four times the maintenance burden, which slows every future change in that area.","correct":false,"tempting":"True and the usual argument, but it describes a gradual cost rather than the specific hazard sitting in this codebase now."},{"text":"Duplication indicates the team lacks a shared understanding of the codebase, and consolidating rebuilds it.","correct":false,"tempting":"Duplication is a symptom of cheap addition rather than of poor understanding, and consolidation does not by itself transfer knowledge."},{"text":"A fix applied to one is not applied to the others, so the dangerous variant survives every correction.","correct":true},{"text":"Fewer helpers means less code for reviewers to read, which relieves the constraint the whole course identifies.","correct":false,"tempting":"Marginal: the helpers already exist and are not re-reviewed, so removal does not reduce ongoing review load much."}]},"prev":{"slug":"planning-verification-as-work","title":"Putting verification in the plan","url":"https://addaly.com/learn/how-software-changed/planning-verification-as-work"},"next":{"slug":"architectural-coherence","title":"Coherence has to be defended by something","url":"https://addaly.com/learn/how-software-changed/architectural-coherence"},"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) 'Scheduling the deletion', in How Software Development Changed. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/how-software-changed/consolidation-and-entropy (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":"Scheduling the deletion","courseTitle":"How Software Development Changed","module":{"index":6,"slug":"teams-and-delivery","title":"Teams, process and delivery"},"answer":null,"keyIdea":"Adding code is easy and locally justified while deleting requires understanding the whole system, so consolidation must be scheduled rather than hoped for, and the argument that works with management is incident arithmetic rather than elegance.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"why-codebases-now-grow-faster-than-they-improve","runs":[{"text":"Why codebases now grow faster than they improve"}]},{"t":"p","runs":[{"text":"It is cheaper to generate a new 200-line helper than to find the existing one, read it, and decide whether it fits. So duplication rises, and nothing in the process pushes back."}]},{"t":"p","runs":[{"text":"The asymmetry is structural rather than cultural. Adding is easy and locally justified: the new code works, the tests pass, the reviewer has no reason to object. Deleting requires knowing that nothing depends on it, which requires understanding the whole system, which is the expensive thing. So the default direction is accumulation, and consolidation only happens if somebody schedules it."}]},{"t":"p","runs":[{"text":"Previously there was an accidental brake: writing code was slow enough that people reused things out of laziness. That brake is gone. Nothing replaced it."}]},{"t":"h2","id":"find-the-duplication-mechanically","runs":[{"text":"Find the duplication mechanically"}]},{"t":"p","runs":[{"text":"You do not have to read the codebase to know where the copies are."}]},{"t":"code","lang":"bash","label":"Bash","text":"# copy-paste detection across most languages, free\nnpx jscpd src/ --min-lines 8 --min-tokens 60 --reporters console\n\n# the cheap version: which function names exist several times?\nrg -o '^\\s*(def|function|func) (\\w+)' -r '$2' --no-filename \\\n  | sort | uniq -c | sort -rn | head -30"},{"t":"p","runs":[{"text":"That second command takes four seconds and routinely finds three date formatters, two retry helpers and four ways of building the same URL. Run it on your own repository before reading further; the result is usually the most persuasive argument in this lesson."}]},{"t":"h2","id":"find-the-dead-code-mechanically","runs":[{"text":"Find the dead code mechanically"}]},{"t":"code","lang":"bash","label":"Bash","text":"vulture src/                 # Python\nnpx ts-prune                 # TypeScript unused exports\nnpx knip                     # unused files, deps and exports\ndeadcode ./...               # Go\ncargo +nightly udeps         # unused Rust dependencies"},{"t":"p","runs":[{"text":"These produce false positives (anything called by reflection, by a framework’s convention, or from a configuration string) so treat the output as a list to check, not a list to delete. Even so, a first run on a codebase that has never had one usually finds several per cent of the files."}]},{"t":"p","runs":[{"text":"The strongest version, if you can do it: coverage instrumentation in production for a week. Code that never executes under real traffic is a much more reliable signal than static analysis, and it catches the endpoint that no longer has any callers."}]},{"t":"h2","id":"make-it-a-scheduled-event","runs":[{"text":"Make it a scheduled event"}]},{"t":"p","runs":[{"text":"Consolidation loses every fair fight against a feature, so do not have the fight. Book the time."}]},{"t":"p","runs":[{"text":"The blunt version that works: "},{"text":"one day a month, everybody, deletion only.","b":true},{"text":" No new features. The rules make it work:"}]},{"t":"ul","items":[[{"text":"Deleting code counts. Deleting a dependency counts double."}],[{"text":"Merging three implementations into one counts."}],[{"text":"Removing a stale feature flag counts."}],[{"text":"Adding anything does not count, unless it is a test that lets you delete something."}]]},{"t":"p","runs":[{"text":"Measure it and publish the number, because it is the one day where lines-of-code-removed is a legitimate metric. A team that removes 4,000 lines and two dependencies in a day has done more for next year’s velocity than a normal feature day, and saying so out loud is what makes people take the next one seriously."}]},{"t":"h2","id":"the-argument-to-management","runs":[{"text":"The argument to management"}]},{"t":"p","runs":[{"text":"Do not argue about elegance. Argue about the numbers you are already tracking."}]},{"t":"quote","runs":[{"text":"Four date formatters means a date bug has to be fixed four times, and last quarter we fixed it twice and missed two. Every duplicate is a permanent multiplier on the cost of every future change in that area, and on the probability that a fix is incomplete."}]},{"t":"quote","runs":[]},{"t":"quote","runs":[{"text":"Deleting the abandoned reporting module removes 6,000 lines, two dependencies with open advisories, and one database table we back up nightly. It reduces our attack surface and our review surface, and it takes one day."}]},{"t":"p","runs":[{"text":"Security and incident arithmetic persuade where aesthetics do not, and both are true."}]},{"t":"h2","id":"the-habit-that-prevents-most-of-it","runs":[{"text":"The habit that prevents most of it"}]},{"t":"p","runs":[{"text":"One norm, in the contributing guide:"}]},{"t":"quote","runs":[{"text":"Before adding a helper, search for one. If you find something close, either use it or write one sentence in the pull request saying why it did not fit."}]},{"t":"p","runs":[{"text":"That sentence is the whole control. It costs the author thirty seconds, it gives the reviewer something specific to check, and it is the only point in the process where the duplication decision is visible to anybody."}]},{"t":"p","runs":[{"text":"And the corresponding norm for the other direction: "},{"text":"every feature flag gets a removal date when it is created, and the removal ticket is created at the same time.","b":true},{"text":" A codebase with two hundred stale flags has a configuration space nobody can reason about, and each individual flag was obviously fine to leave."}]}],"check":{"question":[{"text":"A team runs a duplication detector and finds four near-identical retry helpers, one of which retries non-idempotent POST requests. What is the strongest argument for consolidating them?"}],"options":[{"text":[{"text":"Four implementations means four times the maintenance burden, which slows every future change in that area."}],"correct":false,"tempting":[{"text":"True and the usual argument, but it describes a gradual cost rather than the specific hazard sitting in this codebase now."}]},{"text":[{"text":"Duplication indicates the team lacks a shared understanding of the codebase, and consolidating rebuilds it."}],"correct":false,"tempting":[{"text":"Duplication is a symptom of cheap addition rather than of poor understanding, and consolidation does not by itself transfer knowledge."}]},{"text":[{"text":"A fix applied to one is not applied to the others, so the dangerous variant survives every correction."}],"correct":true,"tempting":null},{"text":[{"text":"Fewer helpers means less code for reviewers to read, which relieves the constraint the whole course identifies."}],"correct":false,"tempting":[{"text":"Marginal: the helpers already exist and are not re-reviewed, so removal does not reduce ongoing review load much."}]}],"order":[3,1,0,2],"why":null},"prev":{"slug":"planning-verification-as-work","title":"Putting verification in the plan","minutes":8},"next":{"slug":"architectural-coherence","title":"Coherence has to be defended by something","minutes":9}}}