We find workarounds that outlived their reasons — closed issues, dead browsers, expired dates. Try it: npx contextdebtcontextdebt.devJoined August 2026
The best workaround comment I've read this year is in Hoppscotch.
"Temporary workaround often get calcified as permanent" — and then the author spends ten lines on why they took it anyway: the complete fix means swapping Record/HashMap for a flat array and propagating that refactor from the networking layer up to the frontend, and you lose the O(1) lookup. HashMap> doesn't shrink the refactor surface much either.
No issue link. No date. Nothing to watch.
That isn't a defect. A note only needs an address when something outside your codebase has to change first — and nothing here is waiting on anyone. It states a reason, a condition, and what it rejected, which is more than most.
packages/hoppscotch-common/src/helpers/kernel/rest/response.ts
An 11-year-old workaround in mongoose:
// Janky hack to work around gh-3005 until we can get rid of the mongoose collection abstraction
Added June 2015. Still in the code today.
And it's still correct. The collection abstraction it's waiting on still exists, so the reason is alive.
Age isn't the signal. An expired reason is. This one stays on hold, not flagged.
A comment in MobX's makeObservable.ts said:
"Hack based on TypeScript#14829 … We need this"
and defined its own NoInfer.
TypeScript shipped NoInfer as a built-in in 5.4 (March 2024).
The hack outlived its reason by 2.5 years, including the MobX 7 rewrite.
I opened an issue asking one thing: keep it, or drop it?
The maintainer answered with the fact I didn't have (MobX already requires TS 5.6) and removed it the next morning.
github.com/mobxjs/mobx/pu…
The reason was dead. The deletion call was theirs.
There's something to this, though I'd put the line somewhere else - the gap doesn't show at ship time, it shows a year later.
And professionals aren't automatically on the right side of it. Sequelize has four TODOs in packages/core/src/model.js dated [>=2023-01-01], written in April 2022 - more than three and a half years past their own date. The repo already installs the ESLint rule that exists precisely to catch that (unicorn/expiring-todo-comments). It fires as a warning, and CI runs eslint with --quiet, which drops warnings.
So the difference isn't professional vs. not. It's whether the expiry date is checked by something that can actually fail the build.
That last line is the whole invoice, and it doesn't arrive for a year.
The cost isn't proportional to lines of code. It's proportional to the number of temporary decisions in there whose reason nobody wrote down - the "just until library X fixes this" ones. Whoever maintains it later can't tell which are still load-bearing without going and checking each upstream themselves.
A real instance we looked at recently: a workaround for a UI library bug landed in Novu in Feb 2025, then got copied into brand-new code again in Aug 2026. We checked - the reason is still valid, the upstream bug is genuinely unfixed. The point isn't that it was wrong. It's that eighteen months and one copy later, nobody had checked.
Agreed - the need to depict structure didn't go away, only the up-front-spec ritual did.
The part that rots, in diagrams and in code comments alike, isn't the notation. It's that both record a reason without recording a condition anyone can check later. "We do X because Y" ages into folklore. "We do X until Y is true" can be re-checked, by a person or by a machine.
Concrete one: jQuery UI still carries "Hotfix for jQuery 1.4" in ui/effect.js, written January 2010. The supported jQuery range was moved up to >=1.12.0 in 2024 - but in package.json, a different file. Nothing ever connected the two, because the note named a fix and not a condition.
The card loses because it's an opinion competing with features. Some of it isn't an opinion, though — a note carrying a date or a version that has already passed is a fact check, not a preference, and that's a much shorter argument in planning. Sequelize still has four TODOs marked [>=2023-01-01] in model.js; its own linter rule would flag them, but CI runs eslint --quiet, so the warning never prints.
Worth noting you wrote the condition down — "until the new agent runs on the new harness" is something someone can actually check. Most keep-it-around notes only record the feeling. The failure mode isn't keeping the old code, it's that nobody comes back to test the condition: jquery-ui still carries a "Hotfix for jQuery 1.4" from Jan 2010, and the repo's own 2024 commit raising supported jQuery to >=1.12 is what retired the reason. The line is still there.
The de-slop pass that pays twice is the one that dedupes workarounds, not just helpers. A duplicated workaround means one dead reason expires in N places at once — and agents keep copying them forward. In novu, a workaround for an upstream cmdk bug landed Feb 2025 and got copied into new code again in Aug 2026, now across three files. npx contextdebt flags the comment shapes; the duplicate count is what makes a sweep worth scheduling.
The side quests usually come from refactors with no stopping condition.
The ones that stay small are the ones where something outside the code already decided it: a version floor that moved, a browser nobody ships, a dated note whose date passed. axios still carries an MSIE check — one branch, one reason, nothing else to touch.
Everything else, the agent has to guess where to stop.
Items that survive most sweeps, because the code still runs:
– comments naming a version floor the project already passed (celery carried pre-3.10 leftovers after requires-python moved to 3.10)
– UA/browser branches for engines nobody ships (axios still checks for MSIE)
– suppressions whose underlying rule no longer fires (noqa, ts-ignore)
– dated notes whose date has passed
I built a small CLI that scans for that shape: npx contextdebt
34% smaller is the part most sweeps never reach — deleting unreachable code is the easy half.
The half that survives every sweep is code that still runs but whose reason expired: axios still branches on an MSIE check, celery carried pre-3.10 leftovers after its own floor had already moved to 3.10. Reachable, so nothing flags them.
Curious whether the 1,393 agents caught any of that class, or mostly the unreachable kind.
Two comments in Sentry's Node SDK said "remove once we drop Node X." The floor moved to 20.19 in July; the notes stayed.
Filed the issue this morning, PR merged this afternoon. Whoever wrote those notes did it right — they left a condition. All we did was come back.
github.com/getsentry/sent…
We read 25,546 workaround notes across 1,813 of GitHub's most-starred repos — probably more than anyone has read on purpose.
About 1 in 20 can ever be acted on. The other nineteen are honest, correct, and permanently stuck.
The difference is three sentences: contextdebt.dev/notes
Yes on the nits — naming and helper-vs-inline wash out. One thing I'd keep on the periodic pass though: workarounds whose reason already died. They survive review because they read as deliberate. axios still branches on an MSIE check; celery had a dependency gated to Python < 3.9 while requires-python was >= 3.10 — it could never install. Shape looks fine either way, so a refactor pass that only looks at shape walks right past them.
Step 5 is where I'd put the thing nobody queues: not messy code, but stale premises. Angular carries "TODO(legacy-partial-output-inputs): Remove in v18." across 7 files; the repo is on 22.x. Nothing fails, no test goes red, so no orchestrator ever files it. The autonomous half is great at work you can describe — the compounding cost is the work nothing reports.
Solid list — and every one of these is machine-checkable, which is why the leftover class is interesting: code that passes all ten and still shouldn't be there. Storybook still ships a Next.js loader patch commented "Remove this when Next.js < 14.1 is no longer supported" — its peerDependency has been ^14.1.0 since 9.0.0. Not dead code, not redundant, fully typed. Just a workaround whose reason expired. That's the class we scan for: npx contextdebt
Following up on my own number here: we re-ran the scan at full scale since, and it went up.
`TODO webpack 6` isn't 8 comments. It's 66, across 37 files - and 41 of them say outright that they should be removed.
Still none are stale, because webpack 6 doesn't exist yet. That's what makes it the clean example: 66 lines already know the condition of their own death, and nothing is watching for it.
The full run, and how we counted: contextdebt.dev/report
Congrats on the release — native CSS is the right call.
One thing from the other side of a deprecation: webpack's own tree carries 8 comments in the shape of "TODO webpack 6 remove this". None of them are stale today — they're waiting on a version that doesn't exist yet, so there's no date on any of them to check against.
The day v6 lands, all 8 come due at once. Today's deprecations will grow the same shape in every downstream repo that pins css-loader.
Closed isn't fixed. A PR nobody merged fixed nothing, and an issue closed "not planned" makes the workaround permanent rather than expired.
That distinction cut 73 numbers from this report before we published it.
Every claim is data you can re-run against us:
contextdebt.dev/claims.json
The oldest one: knockout ships // Workaround for .../knockout/issues/155
That issue is titled "comment-based control flow broken in IE7". Closed 2011-09-13.
Magento 2 bundles a copy of that file - so it's in a store checkout right now, explaining IE7 to your agent.
We scanned 1,813 of the most-starred repos on GitHub. 201M lines.
25,546 comments where the code admits it's a workaround.
Then we asked the 978 issues those comments cite whether they were still open.
679 were already fixed. Median: 4.2 years ago.
contextdebt.dev/report
9 Followers 208 FollowingProfessional Services | AI Agents + Data + CRM | San Francisco Bay Area | prev: Tora (acq. LSEG), CHORI (UCSF), Gladstone Institutes, UC Berkeley Alum
5.4M Followers 4 FollowingOpenAI’s mission is to ensure that artificial general intelligence benefits all of humanity. We’re hiring: https://t.co/dJGr6LgzPA
1.8M Followers 2 FollowingClaude is an AI assistant built by @anthropicai to be safe, accurate, and secure. Talk to Claude on https://t.co/ZhTwG8d1e5 or download the app.
34K Followers 7 FollowingThe official GitHub Changelog feed. Your source for new releases, improvements, security updates, and fixes across GitHub products.
693K Followers 126 FollowingThe nonprofit organization behind the Python programming language. For help with Python code: https://t.co/XDHPttz2Xv
On Mastodon: @[email protected]
424K Followers 49 FollowingTypeScript is a language for application-scale JavaScript development. It's a typed superset of JavaScript that compiles to plain JavaScript.
12K Followers 17 FollowingTHE build solution for modern web and NodeJS applications.
👩💻 https://t.co/MNtYQhKp6Y
👕 https://t.co/LdSMaOKFLV
🔥https://t.co/mX7XZkIwJi
9 Followers 208 FollowingProfessional Services | AI Agents + Data + CRM | San Francisco Bay Area | prev: Tora (acq. LSEG), CHORI (UCSF), Gladstone Institutes, UC Berkeley Alum
4.0M Followers 837 FollowingCo-founder & CEO @Coinbase. Creating more economic freedom in the world. Co-founder @researchhub @newlimit.
Not investment advice.
904K Followers 462 FollowingFather of three, Creator of Ruby on Rails + Omarchy, Co-owner & CTO of 37signals, Shopify director, NYT best-selling author, and Le Mans 24h class-winner.
25K Followers 248 FollowingA normal guy with big ambitions. I run OOKBEE, SIX and 500 tuktuks/ORZON. Fell in love with NFT & its community. CryptoPunk and BAYC Hodler!
340K Followers 299 FollowingA little bit geek, wonk, and nerd. Repeat entrepreneur, recovering lawyer, and former ski instructor. Co-founder & CEO of Cloudflare (NYSE: NET).
788K Followers 891 FollowingFather, husband and these days running Prima Materia - Trying to build things that matter | Founder of Spotify and Neko Health