CheinTian / notes
Clipping · 6 分钟

Slop Debt

Tech debt has rules. You cut a corner to ship on time, you write a comment that says TODO fix this, and six months later someone pays it down. It is annoying but it is bounded. A senior engineer can look at a codebase with tech debt and draw a map of where the bodies are buried.

arpitbhayani.me Arpit Bhayani

📝 摘要

Arpit Bhayani 提出 Slop Debt(烂码债) 概念,区别于传统技术债务。传统技术债务源于工程师的有意决策(如赶工期跳过抽象),是有界的、可追溯的;而 Slop Debt 源于 LLM 生成代码时缺乏决策——没有人选择变量名、错误处理模式或无意义的间接层,模型逐 token 预测而非设计,导致全局不连贯(incoherence)。Slop Debt 与 Tech Debt 的关键差异在于:Tech Debt 线性增长且局部可定位,Slop Debt 指数增长(模型读取已有 slop 并复制坏模式)且弥漫于整个代码库。调试和代码审查在 Slop Debt 面前失效——代码看起来合理,错在无法融入周围上下文,这种错误不会出现在 diff 中。增量重构同样无效,因为 slop 不是集中在某个模块,而是均匀涂抹在每个文件中。一旦 Slop Debt 超过一定规模,诚实的答案不是"清理它",而是"扔掉重写",由人类做架构决策、模型作为工具而非设计师。

核心概念: Slop Debt 是 LLM 批量生成代码导致的全局不连贯性债务,它与 Tech Debt 的本质区别不在于量而在于质——incoherence 而非 mess。

关键技术栈 / 关键要点:

  1. Slop Debt vs Tech Debt — Tech Debt 源于决策(有界、可追溯),Slop Debt 源于缺乏决策(无界、不可追溯)
  2. 指数增长机制 — 模型读取已有 slop 代码作为上下文,复制坏抽象、新增第三种错误处理方式,每一轮生成都教下一轮更快地产出 slop
  3. 调试失效 — 代码无单一形状可追溯,三种重试策略、两种日志规范共存,无人能解释原因
  4. 代码审查盲区 — Slop 天然"看起来合理",错误在于无法融入全局上下文,diff 看不出问题
  5. 重构无法挽救 — Slop 均匀弥漫而非局部集中,清理一个模块无法减少系统混乱,因为相邻模块在同步生成新混乱
  6. 诚实结论 — 目前没有可靠实践能阻止 slop 形成,行业代码生成速度远超保持一致性的纪律发展速度,重写将成为更常见事件

关键结论 / 实践建议: 一旦 Slop Debt 超过临界规模,唯一诚实的应对是"扔掉重写"——由人类做架构决策,模型仅作为设计者指定的形状内的工具使用,而非设计师本身。

Arpit Bhayani

engineering, databases, and systems. always building.

Tech debt has rules. You cut a corner to ship on time, you write a comment that says TODO fix this, and six months later someone pays it down. It is annoying but it is bounded. A senior engineer can look at a codebase with tech debt and draw a map of where the bodies are buried.

Slop debt does not follow these rules. Slop debt is what happens when most of a codebase is generated by an LLM, reviewed by nobody who actually understands it, and merged because the tests passed. It looks like code. It compiles. It even works, for now. But it was never designed, it was predicted, one plausible token at a time, and that difference matters more than most teams realize.

Why It Is Not The Same Thing

Classic tech debt comes from a decision. Someone chose to skip the abstraction, hardcode a value, or duplicate a function because there was not enough time. A decision has a reason attached to it, and a reason can be recovered later. You can ask the person who made the call, or you can infer it from the git history.

Slop debt comes from a lack of decision. Nobody chose the variable names, the error handling pattern, or the layer of indirection that does nothing. The model chose them, independently, for every file, with no memory of what it did in the last file. Two functions that look identical were generated six weeks apart and solve the same problem in two different ways, because there was no architect enforcing consistency, just a person accepting suggestions.

This is the part teams get wrong. They think slop debt is tech debt at a higher volume. It is not. Volume is not the problem. Incoherence is the problem.

Why It Grows Exponentially

Tech debt compounds linearly, roughly. Every new feature built on a messy foundation adds a bit more mess, and the mess grows with the feature count.

Slop debt compounds because the model itself is reading the slop. Once a codebase has a few hundred files of inconsistent, half explained, locally correct but globally incoherent code, every new generation pass uses that code as context. The model pattern matches against what is already there. It copies the bad abstraction because the bad abstraction is what it sees. It adds a third way of doing error handling because it can not tell which of the first two is the intended one. Each generation cycle does not just add debt, it teaches the next cycle to add debt faster, in the same shape.

Humans do this too, to be fair. Junior engineers copy the pattern next to them, good or bad. But a human eventually asks why, gets frustrated, and refactors. A model does not get frustrated. It has no opinion about whether the pattern it is copying is good. It will happily generate the thousandth variation of a bad idea with the same confidence as the first.

The Point Where Debugging Stops Working

With tech debt, when something breaks, an engineer can trace it. The code has a shape, even a bad one, and shapes can be followed.

With slop debt, tracing breaks down because there is no single shape. You open a file to fix a bug and find three different retry strategies, two different logging conventions, and a helper function that duplicates a standard library method for no discoverable reason. Nobody in the room can tell you why, because nobody wrote it with a why in mind. The person who “wrote” it clicked accept.

At this point, code review stops catching things too. Reviewers are pattern matching on plausibility, and slop is, by construction, extremely plausible. It reads correctly. It is only wrong in the way it fails to fit with everything around it, and that kind of wrong does not show up in a diff.

Why Refactoring Does Not Save You

The normal move for tech debt is refactor incrementally. Isolate a module, write tests around it, clean it up, move on. This works because tech debt is local. The mess sits in identifiable places.

Slop debt resists this because it is everywhere at once, at low intensity. There is no single module you can point to and say, this is where the debt lives. It is smeared evenly across the whole codebase, a little bit of incoherence in every file. Refactoring one module does not reduce the system’s confusion, because the module next to it is generating new confusion at the same rate you are cleaning the old kind.

This is the uncomfortable part. Once slop debt has metastasized past a certain size, the honest answer is not “clean it up.” It is “throw it away and start over,” with an actual human making the architectural calls this time, and the model used as a tool inside a shape someone designed, not as the designer.

What Actually Prevents It

I do not have a clean answer here, and I am suspicious of anyone who claims they do. I have seen the review discipline advice, the “treat it like a junior engineer” advice, the “curate the context” advice. Some of it helps at the margins. None of it has actually stopped slop debt from forming in any codebase I have watched closely enough to trust my own judgment on.

I do not like this. I would rather end an article with a tidy set of practices you can adopt on Monday. But the honest position is that we are generating code faster than we are developing the discipline to keep it coherent, and the gap between those two curves is where slop debt lives. More of the industry’s code is going to be written this way, not less, and the tooling for enforcing architectural coherence at that speed does not exist yet in any form I would trust.

So the real answer, for now, is that most teams are going to hit this, most teams are not ready for it, and the rewrite is going to become a much more common event than anyone is currently budgeting for. That is not a solution. It is just where things are heading, and I would rather say that plainly than pretend otherwise.