All Tests Pass but Code Still Decays? Uncle Bob's Open-Source ub-mode: AI Agent TDD, Mutation Testing & 4 Clean Architecture Gates
深度拆解软件工程泰斗 Robert C. Martin (Bob大叔) 最新开源的 AI 编程智能体技能文件 ub-mode:直面 90% 测试覆盖率依然难以维护的工程顽疾。详解 'First Make it Work, Then Make it Right' 三步法、真实驱动的最小改动(ship the smallest edit)、面向用户行为的诚实测试;深度剖析四大配套确定性工具(crapper 复杂度风险评分、dryer 结构指纹去重、mutator 变异测试杀虫、uml-viewer 依赖方向红线可视化);并提炼行为卡、改动卡、结构卡、证据卡四张团队落地质量闸门,重塑 AI 时代的软件工艺。
A comprehensive breakdown of Robert C. Martin's (Uncle Bob) new open-source agent skill file: ub-mode. We confront why 90% test coverage frequently fails to prevent unmaintainable code in AI-assisted workflows. Explore the three-phase methodology ('First Make it Work, Then Make it Right, Practice'), shipping the smallest edit, and behavioral-driven honest TDD. We deep-dive into its 4 companion deterministic tools (crapper for CRAP complexity scoring, dryer for AST structural duplication, mutator for test suite mutation poisoning, and uml-viewer for Clean Architecture dependency boundaries), synthesizing four actionable team gate cards (Behavior, Edit, Structure, Evidence) to elevate modern software craftsmanship.
大家好 我是为什么叫QQ
Hello everyone, I'm WhyQQ.
测试全绿 覆盖率九成 代码照样可能很难维护
All green tests and 90% coverage don't stop code from being a maintenance nightmare.
Bob大叔最近公开的ub-mode
Uncle Bob recently open-sourced ub-mode,
就把这件事摆到了台面上
putting this exact dilemma under the spotlight.
它是一份给编程智能体使用的技能文件
It is a skill file tailored for AI coding agents.
你让它改代码 它必须先跑通 再把代码做对
When asking it to modify code, it must first make it work, then make it right,
还要留下检查证据
and leave behind verifiable proof.
没有“生成完毕 祝你好运”这一步
There is no "generation complete, good luck" cop-out.
你可能会问 一套老派工程规则
You might wonder: how useful are old-school engineering practices
放到AI编程里还有多少用
in the era of AI-driven coding?
生成速度越快 未经检查的改动就越容易混进项目
The faster generation gets, the easier unverified changes slip into a project.
今天我们沿着这份文件和四个配套工具
Today, we'll walk through this skill and its four companion tools
看看一次普通改动怎样经过行为测试
to see how a typical change passes behavioral testing,
复杂度 重复代码和变异测试
complexity checks, duplication detection, and mutation testing.
看完你可以挑其中一道检查
After watching, pick just one of these checks
明天就用在自己的仓库里
and try it in your own codebase tomorrow.
ub-mode的仓库很小 核心只有README和SKILL.md
The ub-mode repository is tiny, centered around README and SKILL.md.
安装时把SKILL.md放进技能目录
To install it, simply place SKILL.md into your agent skills directory
再用 /ub-mode调用
and invoke it via /ub-mode.
它借鉴了poteto-mode和pstack的部分规则
It borrows concepts from poteto-mode and pstack,
又配上确定性工具 文件把过程分成三段
paired with deterministic tools, structuring the process into three phases:
First Make it Work 先得到能验证的结果
First, Make it Work: get a testable, verifiable result.
Then Make it Right再处理原因 结构 测试和重复
Then, Make it Right: address root causes, structure, tests, and duplication.
Practice 管理日志 上下文 工作区和提交
Practice: manage logs, context window, workspace, and commits.
这个顺序能修补AI的工作习惯
This disciplined sequence patches common AI agent pitfalls.
模型容易一次给出大段答案
LLMs tend to spit out massive monolithic diffs in one go,
ub-mode却要求每个小单元都能检查
whereas ub-mode requires every single unit of change to be inspectable.
“能运行”远非终点
"It runs" is far from the finish line.
代码通过运行以后 维护成本 测试质量和架构方向才刚开始接受检查
Once code runs, maintenance cost, test quality, and architectural integrity are only just beginning to be scrutinized.
第一阶段有句很实在的话ship the smallest edit
The first phase emphasizes a very pragmatic rule: "ship the smallest edit".
用能解决当前问题的最小改动交付
Deliver with the absolute minimum edit that solves the current problem.
旁边还有两条配合它
Accompanied by two complementary guidelines:
先把数据结构想清 再写逻辑;
Clarify data structures before writing business logic;
增加新东西以前 先删掉多余的复杂度
And eliminate accidental complexity before adding new capabilities.
假设你给订单系统加一种退款状态
Suppose you are adding a refund status to an order processing system.
省事做法是沿着旧代码继续补if
The lazy shortcut is tacking on another if statement to legacy code.
ub-mode会逼你停下来
ub-mode forces you to pause and ask:
看看订单状态本该怎样表达
How should order state genuinely be modeled?
非法组合能不能直接从类型里消失
Can invalid state combinations be made unrepresentable in the type system?
如果新需求一开始就存在
If this requirement had existed from day one,
你会怎样设计 也值得重新想一遍
how would you have designed it? That is worth rethinking.
重新设计可以分片交付 不必搞无法回退的大改
Redesigns can be delivered in discrete slices without high-risk, irreversible rewrites.
少写几行只是表面收益
Writing fewer lines is merely a surface-level win;
真正省掉的是后续每个分支都要重新理解的成本
the real payoff is eliminating the cognitive overhead of deciphering tangled branches later.
ub-mode对TDD的要求很具体
ub-mode sets very concrete TDD requirements.
改代码以前先写失败测试
Write a failing test before touching production code,
还要确认它因预期原因失败
and confirm it fails for the exact intended reason,
再用最小改动让它通过
then make it pass with the minimal edit.
做不出诚实测试时 别写永远绿色的摆设
If you cannot construct an honest test, do not write an evergreen dummy test.
测试还要贴着用户行为
Tests must also mirror user behavior directly.
用户怎样调用 测试就怎样调用;
Call the API exactly as an end-user would;
用户看到什么结果 测试就检查什么
And assert precisely on the outcomes the user perceives.
一个函数全被改成空实现 测试若仍然通过
If a function is replaced with an empty stub and tests still pass,
这份测试就该删掉
that test is useless and should be deleted.
修bug也按同样思路走 先复现 再处理原因
Bug fixes follow the same discipline: reproduce first, then address the root cause.
只在崩溃点外面包一层保护 让异常不再出现
Wrapping a crash site in a protective try-catch just to suppress exceptions
数据可能依旧错着
leaves underlying data just as corrupted.
判空算不算修复 可以把看法打在公屏上
Does null-checking alone count as a real bug fix? Share your thoughts in the chat.
review遇到这种补丁
When you encounter such band-aids in code review,
多问一句 原始错误发生在哪一步
ask one question: at what stage did the original error originate?
现在的测试能否抓住它 这些架构建议并不玄
Can current tests catch it? These architectural heuristics are not esoteric:
外部数据在入口检查 内部代码可以放心使用;
Validate external data at entry boundaries so internal code can run with confidence;
业务规则尽量放进纯函数;
Encapsulate core business rules inside pure functions;
同样的请求执行两次 结果应当一致
Executing identical requests twice should yield identical results.
比如支付回调重复到达 系统不该重复扣库存
For instance, duplicate payment webhooks must never deduct inventory twice.
崩溃后重试 也要回到同一个最终状态
Retrying after a crash should converge on the exact same terminal state.
共享可变状态则收紧写入权
Strictly restrict write permissions for shared mutable state.
不需要共享的部分各自持有
Keep isolated state private to each component,
确实共享时只允许一个写入者 并用结构保证
and when truly shared, allow only a single writer enforced by structural constraints.
类型系统也参与防错
Leverage the type system for defensive design.
能用“已支付”和“已退款”两种明确状态表达
If distinct states like "Paid" and "Refunded" express the domain,
就别让一串真假字段拼出互相矛盾的组合
do not juggle boolean flags that can form contradictory states.
这样的约束比事后补丁稳定
These structural constraints are far sturdier than retroactive patches,
因为错误输入在边界就被挡住
because invalid inputs are rejected right at the perimeter,
非法状态甚至没有方便的写法
and illegal states cannot even be conveniently expressed in code.
配套工具crapper把复杂度
The companion tool crapper unifies complexity
和覆盖率放进同一个分数
and test coverage into a single risk score.
公式是CRAP等于CC的平方
The formula is: CRAP equals CC squared
乘以一减覆盖率的立方 再加CC CC是圈复杂度
times (1 minus coverage) cubed, plus CC, where CC is cyclomatic complexity.
ub-mode给每个函数设的处理线是10
ub-mode sets the acceptable threshold for each function at 10.
举个简单例子 CC为10 完全没有覆盖 分数是110
For example: CC of 10 with zero coverage yields a CRAP score of 110.
覆盖率达到八成 分数仍有10.8
Even with 80% coverage, the score remains 10.8.
覆盖率继续上升 复杂分支本身还在
Even if coverage climbs higher, complex branching still lingers.
测试数量和结构问题会一起影响维护风险
Test quantity and code structure jointly dictate maintenance risk.
工具给出的处理办法也很朴素
The tool offers straightforward remedies:
拆分函数 补测试 或者两件事一起做
Decompose functions, add targeted tests, or do both.
switch和case不必机械消灭
You do not need to dogmatically eradicate switch-case statements;
文件明确留了判断空间
the guideline leaves room for pragmatic engineering judgment.
看分数要回到具体函数 别拿项目平均值安慰自己
Evaluate scores per function—do not comfort yourself with project-wide averages.
crapper已支持Clojure Java Go TypeScript
crapper already supports Clojure, Java, Go, TypeScript,
Rust和Python 并能读取或生成对应覆盖率报告
Rust, and Python, reading or generating native coverage reports.
但这里有个容易误判的细节
However, there is a nuance prone to misinterpretation:
缺少工具 或者测试运行失败
If tools are missing or the test run fails,
文档规定这种语言按零覆盖处理 快照仍会写出来
the spec treats that language as 0% coverage while still emitting snapshots.
覆盖率报告没提到的函数 也按零覆盖计算
Functions omitted from coverage reports are also calculated as zero coverage.
于是你看到的红分
Thus, a crimson alert score
可能来自代码 也可能来自CI环境没装好
might stem from bad code, or simply an improperly configured CI environment.
排查顺序很重要 先确认基线测试真的跑过
Diagnosis order matters: first verify that baseline tests actually ran,
报告路径和语言工具是否正确
and that report paths and language tooling are configured properly,
再讨论函数该怎么改
before debating how to refactor the code.
使用 --no-coverage时
When passing --no-coverage,
覆盖率和CRAP都会留成N/A
both coverage and CRAP are marked N/A,
也不能拿复杂度结果冒充完整评分
and raw complexity results cannot masquerade as full scores.
指标适合帮你定位问题
Metrics are great for pinpointing problem areas,
跑指标的上下文同样要接受检查
but the context in which metrics run must also be validated.
dryer查的是结构重复
dryer detects structural duplication.
它会把局部变量
It normalizes local variable names,
字段名 字面量等附带信息规范化
field names, and literals into canonical representations,
再比较语法结构的指纹集合
then compares the resulting fingerprint sets of syntax structures.
分数采用Jaccard相似度
The score uses Jaccard similarity.
两个函数一个筛选奇数后加一
If one function filters odd numbers and increments by one,
另一个筛选偶数后减一
while another filters even numbers and decrements by one,
变量名全换了 结构分数仍可能到达1.0
renaming variables will not fool it—the structural score can still hit 1.0.
ub-mode要求处理0.8以上的重复
ub-mode mandates addressing duplications above 0.8,
dryer当前默认报告阈值则是0.82
while dryer's current default reporting threshold is 0.82.
两处口径别记混
Keep those two calibrated thresholds in mind.
默认规则还会跳过不足四行
Default rules also skip code under four lines
或者规范化节点不足二十个的候选
or candidates with fewer than twenty normalized AST nodes,
避免到处抓小片段
preventing excessive false alarms on trivial snippets.
工具给的是审查线索 抽到公共函数以前
The tool surfaces audit leads: before extracting code into shared helpers,
还要看两段代码是否共享同一条业务规则
check whether both snippets actually share the same business concept.
碰巧长得像的两段逻辑 可以各自保留
Accidental structural resemblance can be safely left separate.
变异测试的思路很像给代码故意下毒
Mutation testing works like intentionally poisoning your code.
mutator会把大于改成大于等于
mutator flips greater-than to greater-than-or-equal,
把真改成假 或者交换and和or 再跑测试
toggles booleans, or swaps and with or, then reruns tests.
测试失败或超时 这个变异体被杀死;
If tests fail or time out, the mutant is killed;
测试仍然全绿 它就活下来了
if tests remain green, the mutant survives.
活着的变异体说明你的断言漏掉了一种行为差异
A surviving mutant indicates your assertions missed a critical behavioral difference.
覆盖没有碰到的变异点默认不会执行
Mutations in uncovered lines will not execute by default,
所以覆盖率和变异分数需要一起看
so code coverage and mutation scores must be evaluated together.
ub-mode的动作也很明确
ub-mode prescribes clear remediation:
补未覆盖区域 处理存活变异体 顺手删除死代码
Cover blind spots, eliminate surviving mutants, and purge dead code along the way.
工具后续运行时会采用差分策略
Subsequent tool runs adopt a differential caching strategy:
未变化函数里已经杀死的变异体不必全部重跑
Mutants already killed in untouched functions do not need to rerun.
下次覆盖率让你很安心
Next time high coverage gives you a false sense of security,
可以挑一个比较运算符试试
tweak a single comparison operator
看看测试还能不能抓住它
and see if your test suite actually catches it.
uml-viewer把命名空间画成组件
uml-viewer renders namespaces as visual components,
把依赖方向 CRAP和变异结果叠到图上
overlaying dependency directions, CRAP scores, and mutation results onto diagrams.
你可以逐层点进模块
You can drill down layer by layer into modules
直到源代码 也能新建proposal
down to source code, or create proposals
先试一种分组方案 再决定是否改代码
to test grouping architectures before committing to code changes.
按照整洁架构的依赖规则
Following Clean Architecture dependency rules,
高层内部组件指向低层外部组件时 箭头会标红
arrows turn red whenever high-level policy points to low-level detail.
看图时同样别急
Do not panic when inspecting diagrams:
缺少CRAP或变异数据的模块会被画成红色
Modules missing CRAP or mutation telemetry will also render in red.
这个红色可能只在提醒数据缺席
That red tint might simply signify missing data.
先确认快照齐全 再处理违规依赖
Ensure telemetry snapshots are complete before tackling dependency violations.
它对程序员的价值
Its true value to developers
在于把review里的“我感觉这里耦合有点重”
is transforming vague review remarks like "I feel this is tightly coupled"
变成可指向的模块和箭头
into concrete, pointable modules and dependency arrows.
讨论有了共同对象 改动范围也更容易收敛
With a shared visual reference, discussions become focused and change scopes converge.
很多人会觉得 这些规则会拖慢智能体
Many people assume these rigorous rules will slow AI agents down.
短任务里确实多几步 可任务一旦牵涉多个模块
For micro-tasks they add a few steps; but once work spans multiple modules,
无边界的大diff会把成本推给review和返工
unbounded massive diffs push huge costs onto code reviews and rework.
AI还特别擅长生成看起来合理的测试
LLMs are notoriously good at generating plausible-looking tests,
测试若只照着实现复述 绿色也证明不了用户行为
but if tests merely parrot the implementation, passing tests prove zero user behavior.
ub-mode把约束放在执行过程中
ub-mode embeds guardrails directly into the execution loop:
小步修改 每步验证 行为测试在前 质量工具在后
incremental edits, continuous verification, behavioral tests upfront, quality tools behind.
看到这里 你可能想反驳
At this point, you might object:
严格门禁会不会让探索变慢
Will strict gatekeeping slow down early exploration?
把你的看法打在公屏上
Share your take in the comments!
我自己的用法会分场景 原型阶段保留快速试验
My own workflow separates stages: rapid prototyping for early experiments,
进入共享仓库以后 试验代码也得经过同一套检查
but once merging into a shared repo, experimental code must pass the same rigor.
速度可以留给生成 合入资格要靠证据争取
Leave speed to generation; earn merge rights with verifiable evidence.
我把整套方法收成四张检查卡
I have synthesized the entire methodology into four gate cards
方便直接放进团队流程
for seamless integration into team workflows:
行为卡 先写能复现问题的用户级测试
Behavior Card: write a user-level test reproducing the issue first,
并确认它真的会失败
and ensure it fails as expected.
改动卡 只交付当前可验证的小单元
Edit Card: deliver only small, currently verifiable units of change;
临时破坏必须事先计划且可回退
temporary disruptions must be pre-planned and easily rollable.
结构卡 检查入口边界
Structure Card: audit perimeter boundaries,
重复实现 依赖方向和函数复杂度
code duplication, dependency direction, and function complexity.
证据卡 跑相关测试与变异检查
Evidence Card: run targeted tests and mutation checks,
在提交里写清改了什么 以及怎样证明
documenting in the commit what changed and how it was proven.
顺序不用机械固定 四个问题最好别缺席
The sequence does not have to be rigid, but never skip any of the four gates.
遇到红灯 再加一个分类动作
When encountering red lights, add a triage step:
先分清环境失败 指标缺失和代码问题
Distinguish environment failure, telemetry absence, and genuine code defects.
这样做的好处很具体
The practical payoff is immediate:
review不再围着个人口味打转
Code reviews stop revolving around subjective tastes;
大家讨论的是哪张卡缺证据 下一步怎样补齐
discussions focus on which card lacks evidence and how to fulfill it.
ub-mode对日常习惯的要求 和工具同等重要
ub-mode's discipline for daily habits is just as vital as its tools:
每次行动记录原因 完成后交出日志;
Log the rationale for every action and produce a run log on completion;
主上下文只留摘要 大段材料放到别处;
Keep only summaries in primary context, offloading bulky artifacts elsewhere;
清理工作区以前确认内容仍在使用
Confirm files are no longer needed before cleaning workspaces,
未提交改动等待决定
and leave uncommitted edits explicit pending decisions.
提交则保持小而有顺序
Keep git commits small and chronologically coherent,
说明动机 变化和验证方式
clarifying motivation, change scope, and verification steps.
文档还写到结束时推向主线
The skill documentation also specifies pushing to main at completion;
团队若有PR 评审和分支保护
if your team relies on PR reviews and branch protection rules,
当然应该服从项目流程
you should naturally adhere to your team's established protocol.
别拿技能文件绕过协作约定
Never use an agent skill to bypass team collaboration agreements.
未来智能体接管更多重复修改后
As agents take over more repetitive engineering tasks in the future,
这些记录会成为排查问题的接口
these disciplined records become our primary debugging interface.
你能看见它做了哪一步 哪项检查曾经失败
You can trace every step it took and which check failed previously,
也更容易只回退有问题的提交
making it trivial to roll back only problematic commits
把已验证的工作留下来
while preserving sound, verified work.
想试这套方法 不必一次装齐所有工具
To adopt this approach, you do not need to install every tool all at once.
明天挑一个新发现的小bug 先写用户行为测试
Pick a newly discovered bug tomorrow: write a user behavioral test first,
复现旧错误 再做最小修复
reproduce the old bug, and apply the minimal fix.
只针对本次改动运行覆盖率和变异检查
Run coverage and mutation checks scoped strictly to this change,
看看有没有存活变异体
to see if any mutants survive.
再用dryer查一下相关文件
Then run dryer over affected files,
提交信息里补上验证命令
and include the exact verification command in your commit message.
review的人就能重复你的过程
Your reviewer will be able to replicate your exact process.
等这套小循环跑顺 再接CRAP阈值和UML依赖检查
Once this tight loop feels natural, plug in CRAP thresholds and UML dependency checks.
代码能跑 只完成了半场
Working code is only halftime.
你更愿意先加行为测试 还是先跑变异测试
Would you rather start with behavioral tests or mutation tests first?
欢迎在评论区聊聊你的仓库现状
Feel free to share the current state of your codebase in the comments.
感谢观看 如果这份检查流程对你有用
Thanks for watching! If this inspection workflow helps you,
记得一键三连
don't forget to like, subscribe, and share.
我是为什么叫QQ,我们下期见
I'm WhyQQ. See you next time!

大家好 我是为什么叫QQ
Hello everyone, I'm WhyQQ.
深度拆解软件工程泰斗 Robert C. Martin (Bob大叔) 最新开源的 AI 编程智能体技能文件 ub-mode:直面 90% 测试覆盖率依然难以维护的工程顽疾。详解 'First Make it Work, Then Make it Right' 三步法、真实驱动的最小改动(ship the smallest edit)、面向用户行为的诚实测试;深度剖析四大配套确定性工具(crapper 复杂度风险评分、dryer 结构指纹去重、mutator 变异测试杀虫、uml-viewer 依赖方向红线可视化);并提炼行为卡、改动卡、结构卡、证据卡四张团队落地质量闸门,重塑 AI 时代的软件工艺。