lolspider / notes
Clipping · 13 分钟

Trust Doesn't Live in Code Review

The gate you becameAgents write code faster than anyone can read it. This is not a forecast; it is the baseline now. A single agent, in an a...

x.com @zty0826

📝 摘要

本文论证在 AI Agent 大规模编写代码的时代,传统代码审查已无法承载真正的信任。Agent 产码速度远超人类审读速度,逐行审查要么成为团队瓶颈,要么沦为形式主义"盖章"——制造虚假信任而非真实信心。作者指出信任的本质是对系统的可预测性,而非对某个 diff 的逐行阅读;Code Review 只是被误认为信任本身的代理手段。

核心概念: 信任是系统的一种状态(可预测性),而非一次审查事件。人类工程师应从"守门人"转向"系统信号读者和输入收紧者"——通过持续信号读取系统熵的走向,将每次失败作为收紧输入条件的诊断输入。

关键技术栈 / 关键要点:

  1. 信任 ≠ Code Review — 信任来自对系统的可预测性(测试套件、仪表盘、历史记录),审查只是其中较弱的渠道;审查是事件,信任是状态,事件无法承载状态
  2. Agent 时代的成本模型差异 — Agent 以近乎零边际成本迭代,人类要么成为慢车道让理解力消退,要么手工写入但破坏由 Agent 成本模型塑造的系统一致性
  3. 软件熵加速 — Agent 将代码腐化的温度大幅提高:一小时搭建可用产品,一天即可让维护性崩塌;熵的方向只向上,需主动投入才能抑制
  4. 从读 diff 到读系统 — 信任不应建立在逐行审查上,而应通过 bug 列表聚类、变异测试(surviving mutant = 测试套件未覆盖的错误行为)、静态结构分析、变更碰撞检测等持续信号来读取系统整体健康状况
  5. 失败是诊断输入 — Agent 的错误不是模型能力不足的判决,而是指向你留下的不清晰输入的标记:模糊规格、误导边界、缺失测试表面;将失败视为输入收紧的闭环
  6. Type 系统和不变量优于事后检查 — 最强手段是让非法状态不可表示(encode illegal state as unrepresentable type),而非事后人工审查

关键结论 / 实践建议: 放弃守门人角色,转为持续读取系统信号并收紧输入条件的闭环实践——每轮失败都用来让条件更真实,而非更用力地审查下一个 diff。


The gate you became

Agents write code faster than anyone can read it. This is not a forecast; it is the baseline now. A single agent, in an afternoon, ships more code than a team reads carefully in a week. Code review quietly assumed writing and reading happened at the same speed. For the first time, they do not.

So you get two bad choices. Hold the line and read everything, and you become the throttle the whole team queues behind. Wave it through and review becomes a stamp. Most teams quietly chose the stamp. The worst part is not the bugs that slip past; a missed bug is recoverable. False confidence is not, and it is manufactured on schedule. The stamp still produces the feeling of trust, and you ship on that feeling. You are not failing to build trust. You are building a fake one.

But step back, because the gate was never the point. What you need is not the reading. It is control over the project and grounded trust in it, the standing confidence that you know what you are shipping. Reading the diff was only ever the proxy, mistaken for the thing itself. What cannot be outsourced is the control and the trust. Those stay yours. The reading can go to whoever, or whatever, is best placed to do it.

You can't write it, so you can't read it

Start with why your reading was ever worth anything, and it is not that you are careful. A review is only as good as the developer doing it, and what makes a developer good is one thing: their hands are in the code often enough to hold its details live. That live understanding is the engine of a review; the reading is just where it shows up. Take it away and the reading is empty motion, eyes moving over lines.

The agent era pulls you out of being that developer, for one reason that surfaces two ways: you and an agent do not pay for code the same way. The cost model is different, and everything follows from that.

In real time, the gap reads as speed. An agent iterates at nearly zero marginal cost; a human writing by hand among them becomes the slow lane the work waits behind. The honest move is to stop and let the agents run, and the moment you do, your live understanding fades, because you are no longer building it.

Over the codebase, the same gap reads as coherence, and this holds even for a human fast enough to keep up. A codebase is the crystallization of one cost model's thousand small judgments: whether a duplication earns a refactor, how much logging is worth the noise, where a boundary actually falls. Agents hold that rigor evenly, without the fatigue or the boredom that leads a tired human to cut the logging or skip the refactor. A human reaching in by hand is not writing worse code, just code optimized for a different cost model, and dropping it into a system tuned to another splits the coherence that made the system legible. The high level stays yours: what the system is for, where it can go, which risks are worth foreseeing, which tradeoffs worth making. The implementation grain underneath settles into whoever is paying for the code.

Put it plainly, inside a specific regime we are building toward rather than one that already governs everywhere: once a project has gone agent-native, with agents at least as strong as you in the domain and a codebase already in their grain, a human can no longer really write the code, by pace or by grain. And a human who cannot write the code cannot hold it live enough to review it. Not a law of nature, but inside that regime the conclusion is firm: keep reviewing and you are running your eyes over lines you no longer have the standing to judge. It is also why, when the agents have not sped your project up, the bottleneck is almost always the one human still writing and reading by hand. Not the trust, not the sense of direction; those stay. The bottleneck is the hand still on the keyboard.

None of this is new; the agent era only made it impossible to ignore. Trust never lived in code review, not now and not before it. The person steering a large project does not read the code: when did the most senior engineer you know last read, line by line, the system they are responsible for? They steer on trust in the people below them and a read of the rough architecture, and nobody calls that reckless. Line-by-line reading was something the lower levels could afford, and the affording got mistaken for the trusting. The agent era just pushes every level into the position the top was always in.

So where did the trust come from? From an author you had worked with long enough to predict, a test suite that held under load, a partner who had watched the system run a hundred times, a beta that came back clean, dashboards quiet across three releases. Code review was always one of the weaker channels in that ensemble. What it kept gesturing at, and never quite delivering, was an answer to one question: is the system as a whole still holding together? That question does not go away when the volume rises. It gets sharper, and it needs an instrument that was never the diff.

Faster code, faster rot

As a system ages under change, the number of programs that could pass for yours keeps growing. In a young, tight system the survivors are nearly the same program. Let it age without care and the family sprawls: the same spec, the same green checks, an enormous space of mutually inconsistent implementations that all pass. That sprawl is a kind of entropy, and what makes it the right word is direction: left alone, it only grows. Every sharp boundary and every honest test cuts it down; every shortcut and every duplicated source of truth lets it grow back.

None of this is new either. Lehman wrote it down in the 1970s: a program in use keeps gaining complexity, release over release, unless someone spends real work to hold it back. Software rot, code decay; the field has had words for it as long as it has shipped software. What is new is not the phenomenon. It is the temperature. A working product can be stood up in an hour and left unmaintainable a day later, at the same speed either way. Agents did not invent entropy; they just turned the temperature up, removing what used to hold the rate down: how fast a human could write and read code.

You feel rising entropy as one loss: you can no longer predict your own system. You cannot say, before you change something here, what it will do over there. Trust in a system was always just this: the ability to predict it. Not a feeling, not a green check: the earned confidence that you know how the thing behaves. A diff cannot give that back, because a diff shows one change, and prediction is a property of the whole. So the question is not whether a given diff is correct. It is whether the entropy of the whole is rising or held. That is readable, and reading it takes no diffs at all.

Stop reading the diff

So how do you read entropy without opening every diff? A diff shows you a change; it can never show you a system. You stop reading the diff, and start reading the system. The test suites and dashboards were where trust came from; the signals below are how you read it now, live, while the system runs.

Review reads an artifact: the change in front of you, line by line. But what you want to know is not a property of any one change. It is a property of the whole system, and the whole system is already telling you about itself, all the time, without anyone opening a file. It emits signals; you read those instead. That is why it works where review could not: review was pointed at the artifact, and what you needed to know was never in the artifact.

The bug list is the most direct, because it is not a forecast: it is the record of where the system has already gone wrong. Bugs cluster, and the clustering is the map. The same module surfaces week after week, the same contract grows a new edge case every month, because that is where the system leaks. Agent failures are especially legible, because an agent's mistake usually points back at an input that was not clean: an ambiguous contract, a spec that left a case unsaid. A human compensates out of habit and hides the gap; an agent walks into it and marks the spot.

Other signals report where it could still break. Mutation testing flips a condition or deletes a branch in working code and asks whether any test goes red; a surviving mutant is a wrong version of your program your suite still blesses, the exact gap between "the tests pass" and "the behavior is pinned." Static structure shows where breakage starts: a function thick with branches has more states than its spec needs, a fact stored in two places invites the day someone updates one and forgets the other. And where agents work alongside each other, one signal is real-time: whether their changes collide. The collision that matters is not two agents touching adjacent code, which is ordinary concurrency; it is the same seam fought over again and again, which means the boundary there is wrong.

The map also tells you where to aim. Where the bug list is dense and the branches deepest is where the unknown bugs are densest too, so that is where you point the test framework: not to pad a coverage number, but to build the cases that flush the failures hiding in the tangled regions. A found bug only counts once a test goes from red to green on it, so the fix is pinned and cannot quietly return.

But a green test is not free quality, and this is where most suites lie to you. A test earns its green only if its check is hard: it has to assert against real behavior, on a real payload, so a true regression turns it red, and it has to be complete across the paths the behavior runs, because a check that passes on one path while another goes unexercised is an untested case in a passing test's clothes. A test that checks the shape of the code instead of its effect is a green light wired to nothing. The same suspicion falls on every signal. A quiet bug list can mean the module is healthy, or that no one exercises it, or that its instrumentation went dark. An absent signal means something only if you have established it would have fired. So you hold each instrument to the standard you were about to drop for the diff: trusted only once you have proven it can fail. Quality lives in how hard your checks are, never in anyone having read the lines.

Two honesties before you lean on this. First, most of these readings are trailing: the bug list is where it already broke, the surviving mutant is a gap in tests you already have. They catch the rot a beat after it lands, which beats a stamp that never catches it, but it is not prevention; prevention moves upstream, into the inputs. And no single reading is the whole picture, so you read several and let them triangulate. Second, not all high entropy is decay. A plugin system where a hundred extensions satisfy one interface is, by the count, enormously high-entropy, and that is the design working as intended. The signals show you where the entropy is; they do not tell you which kind. That judgment does not come for free.

A review is an event; trust is a state

All of this had to move off the diff for a reason deeper than speed: a review is the wrong shape for what you are trying to hold. A review is an event. Trust is a state. The event happens once, leaves a verdict, and is gone; nothing accumulates in the system you are trying to trust. The state is what you actually want, the standing confidence that you can predict your own system, and you cannot build a state out of events that leave nothing behind. That is the sentence the title was pointing at: trust does not live in code review because an event cannot house a state. Run the event a thousand times and you still do not have the thing. The signals are continuous, so trust accumulates from them; the stamp never could.

So review, as your trust instrument, leaves. Reading code does not vanish; it moves to where the understanding lives, which in the agent era is the agents writing the code, not you. They are the ones with their hands in the details, round after round. What leaves is you, doing it as the gate. And the high-stakes cases people reach for first, the security boundary, the irreversible release, are exactly the ones a tired human scan was always worst at. They ride on stronger things now: a property the type system makes impossible to violate, a check whose assertion is hard, a staged rollout watched by monitoring that catches the regression a reading would miss.

Here reading the signals becomes more than watching. Each one points back at a cause. A surviving mutant points at behavior nobody pinned. A drifted boundary points at a contract that stopped meaning one thing. A cluster in the bug list points deeper: this code was written by a capable model, so when a module keeps generating bugs, the likeliest cause is not that the model is weak there. It is that the module's interfaces mislead anything working against them. The bug is the system telling you the boundary is wrong. So you refactor it under the test framework you just built, which holds the behavior steady, so every change is either provably behavior-preserving or a real bug flushed into the open.

The move is almost never "review the next diff harder." It is to treat the failure as what it is: diagnostic input for the loop. A failure is not a verdict on the model; it is a map of the inputs you left unclean. It tells you, precisely, which condition you gave it was not yet clean, and once you see it that way the agent's mistakes become the most valuable thing it produces: each one a labeled pointer at a gap you could not otherwise see.

This is easiest to feel by role. The agent that designs the architecture fails, and the cause is rarely its reasoning; it is that the mission, the direction you are building toward, was never made clear enough at the input. The agent that writes the implementation fails, and the cause is rarely intelligence; it is that the environment, the test surface, the validation harness were not in place, or the boundaries you drew misled it. The agent that tests against your intent misses what mattered, because the intent was never expressed clearly enough to test against. The failure never terminates at the agent. It travels up the chain and lands on an input: something you said unclearly, a surface you did not build, a boundary you left soft.

That is the loop closing. You read where the entropy is rising, trace the failure to the input that let it rise, and tighten that input. Sometimes that means a sharper spec; sometimes refactoring the contract that misled the model; and at the strongest, changing the design so the failure cannot occur at all: a type that makes the bad state unrepresentable, an invariant enforced by construction rather than checked after the fact. That last move only reaches the failures you can encode as an illegal state. The external and timing ones, a credential that never answers, a race between two services, stay irreducible, and fall back to the hard checks and staged rollouts from before. The system gets more predictable not because anyone reviewed more code, but because each failure was spent making the conditions truer.

That is the practice in one sentence: stop guarding the gate, and start reading the system and tightening its inputs, round after round. It is the only thing we have found that keeps quality and speed in the same hand once the agents move faster than anyone can read.

Which leaves one question, the one this whole article has been walking toward. If you are no longer the gate, no longer even the one who reads the code, if your day is reading the system and tightening what feeds it, then what, exactly, is the human's job now? That is the next article.