CHAPTER 5 · EP 30

质量底线 · 文档与沉淀

第 5 章 · 第 30 讲 · 15:49

时长 15:49音色 云健 · 男声章节 5

同步字幕

章节导航(点击跳转)

0:00开场 · 能跑之后,才轮到质量1:25
1:25一 · 样式收敛,别让一个按钮长出八套样式2:42
4:08二 · 注释三要素,把决策留给三个月后的人1:50
5:58三 · 代码保护,别让顺手清理变成破坏1:27
7:25四 · 调试铁律,先日志再改码2:35
10:01五 · 不接受分期交付2:07
12:09六 · 三份文档加一本方法论1:54
14:03可带走的判断清单1:45
解读全文

ep30 · 质量底线 · 文档与沉淀

模块:M5 工程可行性 | 篇章:质量底线
本集解决「能跑之后怎么别烂掉」:样式收敛、注释结构、代码保护、调试纪律、交付纪律、文档沉淀。
来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)

一、本集定位:从「能跑」到「不烂」

前几集解决的是「能不能跑起来」。这一集解决的是跑起来之后别烂掉。典型症状有四组:

  • 样式散成一片,改一个按钮要动八个文件
  • 注释等于没写,三个月后想不起当初为什么这样实现
  • 改个问题猜三轮,改完还引入新问题
  • 一句「后续再优化」,在九十天里长成永久技术债

这四组症状都不是能力问题,是纪律问题。纪律的特点是:靠人不靠模型——不能指望它每次都自觉,只能把闸设在动手之前。

为什么产品经理要关心技术债

技术债会以最直接的方式反映在产品上:

技术债表现产品侧后果
样式散装改一个按钮动八个文件,视觉一致性失控
注释缺失新人接手周期拉长,决策无法追溯
猜测式修复修一个问题冒两个新问题,线上事故率上升
分期交付补全成本随时间指数上升,超过重写

你在需求评审上省下的半小时,最后会以十倍的返工还回来。


二、能力地图:六个环节 × 一道闸

环节核心问题要设的那道闸
样式收敛一个按钮长出八套样式写新样式前先搜变量与公共组件
注释结构代码只能表达「做了什么」三要素模板:背景、设计意图、关键约束
代码保护顺手清理变成破坏删除前必须声明理由并等许可
调试纪律猜一个原因改改看禁止猜测性修复,第一步永远加日志
交付纪律先做简版,后续再加只接受完整方案,拆分由人决定
文档沉淀决策随对话消失四份文档进仓库,位置即架构

六个环节有一个共同结构:先建立事实,再动手,最后留下记录。 缺任何一环,质量都守不住。


三、样式收敛:一个按钮不要八套样式

为什么会增殖

重复造样式不是模型偷懒,是三个结构性原因叠出来的:

  1. 看不见。 新开一轮对话,上下文里只有这次给的几个文件。项目里已经有一个主按钮样式这件事,它无从得知,于是按需求现写一个。
  2. 更省事。 读懂一套现有样式要把相关文件全看一遍,还得担心改了影响别处。新起一个名字零风险、零阅读成本——这是它的最优解,不是你的。
  3. 不敢碰。 需要带阴影的按钮时,它宁可再写一个新样式,也不去改原来那个。改动别人在用的样式属于高风险操作,它选择了安全但会增殖的做法。

三个原因指向同一件事:它缺一份「我们已经有什么」的清单。 把这份清单写进项目规则文件,它每轮都能看见,增殖才会停。

收敛四步

步骤动作关键约束
一 · 盘点扫全项目样式,产出重复清单先只看不改,不许动代码
二 · 定变量主色、语义色、圆角两三档、间距四档、控件高度档位要少,少才守得住
三 · 分批合并一次一种控件,先按钮验证,再卡片,再输入框每批单独提交,可单独回滚
四 · 设闸写进规则文件:先搜变量和公共组件不设闸,三个月后还要再收一遍

一条判据:差异有没有名字

判断该不该合并,只问一句:这个差异叫什么?

  • 叫得出名字的留着——「次要按钮」「危险操作」「触摸目标下限」,这些是设计决策,理由写进注释即可。
  • 叫不出名字的合掉——两个差 2px 的圆角、两个肉眼分不出的蓝,它们不叫什么,是当时随手写的。

一句话版本:差异不是罪,没理由才是。

三个坑

  1. 一把梭全量重构。 让「把全站样式统一一下」,会拿到一个改了 60 个文件的 diff,审不完也不敢发。永远按控件分批。
  2. 顺手改视觉。 收敛过程中它常「顺便优化」圆角和配色,让你分不清页面变样是合并出的 bug 还是它的审美发挥。收敛只做等价替换。
  3. 变量定太细。 定出 12 档圆角、9 种灰,等于没定——下次它还是要挑,挑就会挑错。

提示1 · 收敛必须做等价替换

样式收敛是结构手术,不是视觉改版。验收标准只有一条:收敛前后,页面在肉眼上应该看不出变化。任何视觉上的改变,都必须拆成另一个独立任务、单独评审、单独回滚。把这两件事混在一个提交里,出问题时你无法判断到底是合并引入的 bug,还是它的审美发挥——排查成本会翻好几倍。


四、注释三要素与代码保护

问题在哪

代码只能表达「做了什么」。为什么存在、为什么这样实现、调用时要注意什么,这些信息只有写进注释才能跨时间留存。而「写好注释」四个字它执行不了,必须给出固定结构和示例。

三要素结构

要素回答什么缺失的后果
背景为解决什么业务问题、在什么场景被调用读代码的人只看到实现,看不到它为什么存在
设计意图为什么这样实现、放弃了哪些备选方案版本记录里找不到,注释是唯一载体
关键约束副作用、依赖关系、边界条件下一个调用者就会踩坑

对比:同一个函数的两种注释

差的注释:只写一句「合并两个聊天记录列表,返回合并后的结果」。这等于把函数名又念了一遍,读一眼代码就能得到同样的信息。

好的注释:写三件事——

  • 背景:前端每次会话恢复会带上本地缓存的历史消息,服务端也保留一份持久化记录,两者可能因网络中断出现分叉。
  • 设计意图:以服务端记录为权威,只追加本地独有的消息,不做全量替换,避免覆盖掉服务端已有的回复元数据。
  • 约束:本地传来的系统角色消息一律丢弃,不合并入结果。

三个月后你想知道「为什么以服务端为权威」「为什么丢弃系统消息」,答案都在里面。

判断标准

只有一条:未来接手的人,没有这条注释,还能理解当初为什么这样做吗?

两条保护规则

  1. 注释保护。 重构时禁止以「注释太长」「代码自解释」「顺便清理」为由删除背景和设计意图注释。实现变了导致注释不准确时,必须同步更新内容。
  2. 代码删除声明。 删除任何已有功能代码前,必须明确告知并说明理由,禁止以「顺手清理」「看起来没用」为由静默删除。认为某段代码该移除时,先标注 // TODO: 建议移除 - 原因:xxx,拿到许可再删。

配套规范:禁止空捕获

所有异常捕获和错误分支必须有实质性处理:日志记录加用户可见的错误提示,或合理的降级逻辑。仅打印一行日志、直接跳过、写一句忽略,都属于静默吞错,一律不允许。

原因很实在:静默吞错会让问题在离现场很远的地方爆发,排查成本成倍上升。

提示2 · 「看起来没用」和「确认没用」之间隔着线上事故

AI 重构时发现一段兼容旧数据格式的代码,觉得「看起来没用」,想顺手删掉。正确做法不是删,也不是原样留着,而是标注建议移除并说明理由,等确认。这条规则的价值不在于防住多少次删除,而在于它把「删除」从一个可以顺手的动作,变成了一个必须留痕的动作。留痕之后,责任归属和决策依据都清晰了。


五、调试铁律:先 Log 再改码

核心条款

禁止猜测性修复。 无法确认根因时,必须先通过日志、断点或测试脚本验证假设,禁止「试着改一下看看」。后端在终端打详细日志,前端在浏览器控制台打日志——无论什么问题,第一步都是加日志。

两条修 Bug 路径的对比

Bug 现场:聊天输入框在中文输入法下,用户按回车确认候选词时,半截拼音被当成消息直接发了出去。

  • 猜测性修复路径:先猜是回车事件没拦截,加一层判断;再猜是输入法合成状态没处理,再加一层;改了三轮,动了几十行,问题可能还在,还引入了新问题。
  • 先加日志路径:在按键事件里打印出合成状态和事件类型,一眼就能看到「合成中的按键也被当成确认处理了」,一次改对。

加日志的两分钟,买断的是猜错三轮的返工,还有被掩盖的根因。

修复前三问

以「用户删除一条聊天记录后,会话列表未读数没有更新」为例:

问题本例答案
一 · 链路是什么删除消息 → 更新会话摘要 → 重算未读数 → 推送列表刷新;排查发现「重算未读数」只在收到新消息时触发,删除路径根本没走到它
二 · 会波及谁会话列表、App 角标、多端消息同步三处
三 · 同一个坑还有没有别处有。「标记已读」和「撤回消息」走同一条链路,同样漏了触发重算,一起修干净

只盯着报错点,是看不到这条链路的。

修复后:声明影响范围

格式:⚡ 影响范围:XXX、YYY、ZZZ。让人知道该回归测试哪些地方。

交付线:两道硬性检查

  1. 禁止 Mock 绕过真实 AI 接口。 凡涉及模型调用的功能,交付前必须确认接口真的能访问。用户没给 API Key 时必须停下来要,禁止硬编码假响应或本地模拟绕过真实调用。Key 到位后先发一次测试请求验证可用性,再继续开发。
  2. 单元测试不过,不得交付。 核心业务逻辑、API 接口、数据处理函数、边界条件都要覆盖。测试文件统一放 tests/,命名 test_{模块名}.py。调试用的临时脚本,用完自行删除。

提示3 · 影响范围声明是给别人用的,不是给自己看的

修完 Bug 之后的「影响范围声明」,很多人觉得是形式主义就跳过了。它的真实价值在于:它把「这次改动会不会伤到别处」这个问题,从修复者的脑子里,转移到了回归测试者的清单上。 不写声明,回归测试只能凭经验猜要测什么;写了声明,测试范围就是可枚举的。这一步花十秒钟,省的是上线后的一次回滚。


六、不接受分期交付

现象

你要一个完整的认证系统,它说「先做一个简版的用户名密码登录,后续再加 OAuth」。后续永远不会来。

真实动机

实践中它说「先做简版」往往与复杂度无关——它想快速给你一个能跑的东西,来换取正反馈。「先用临时方案」「暂时 Mock」「简单处理一下」背后是同一个模式。

技术债时间线

时点发生什么成本变化
第 1 天简版上线,承诺「OAuth 后续再加」补全只要 0.5 倍成本,可惜没人回头
第 30 天新需求源源不断,没人回头补;会话、权限、支付、通知 4 个模块直接依赖简版接口补全成本升到 3 倍
第 90 天依赖长死,9 个模块与简版耦合,重构成本高过重写补全成本 8 倍,超过重写

一句「后续再优化」,在九十天里长成了永久技术债。

规则与替代方案

禁止的做法:以任何理由简化实现——「先用临时方案」「后续再优化」「暂时 Mock」「简单处理一下」全部不接受;也禁止它主动规划分期、MVP、阶段一二三。每次实现都必须是完整、正确、没有代码债的方案。

替代的做法:评估一个功能只回答两个问题——完整做下来需要什么、有多复杂。确实太复杂时,明确列出「需要你先做哪些前置决策」,把选择权交还给人。已知有缺陷的方案,直接给正确版本。

适用边界

大型项目里这条规则可能显得激进:一个功能真需要 2000 行代码时,一次写完不现实。此时正确动作依然成立——让它给出完整方案和真实工作量,由人决定是否拆分、怎么拆分。

拆分是人的决策,降级是 AI 的自作主张。 这两者的区别,就是这条规则的核心。

提示4 · 把「要不要拆」这个问题收回到人手里

「不接受分期交付」最容易被误读成「所有功能都必须一次写完」。它真正的意思是决策权归属:AI 可以并且应该给出完整方案与真实工作量,但「拆不拆、怎么拆」必须由人拍板。区分信号很简单——如果它是在回答你「这个太大了」的质疑时给出拆分建议,那是人主导;如果它在你还不知道有分期这回事时,就主动把方案砍成「先做简版」,那就是降级。前者可接受,后者要拦。


七、三份文档与一本方法论

为什么需要

做了 30 个功能,三个月后想查「这个功能什么时候加的、当初为什么这样设计、中间改过几次方案」,翻遍 git log 也找不到。

四份文档各管一个维度

文档回答的问题关键规则
docs/FEATURES.md这个功能怎么来的功能点唯一事实来源;状态流转 🟡 规划中 → 🔵 开发中 → 🟢 已完成 / ⚪ 已取消;取消的功能不删,标 ⚪ 并注明原因
docs/CHANGELOG.md这次改了什么按时间倒序,表格记录问题/需求、根因/方案、改动范围、影响面、状态;类型标签 BUG / FEAT / REFACTOR / PERF / DOCS;写之前必须读系统时间
docs/RELEASE_NOTES.md用户得到了什么面向真实用户,语言风格与 CHANGELOG 完全不同;每条必须能回答「这对我有什么用」
docs/METHODOLOGY.md我们是怎么想的四段结构:产品原则、设计决策记录、用户体验偏好、反模式

两条容易违反的红线

  • CHANGELOG:禁止凭记忆填时间戳,禁止积压补写。
  • RELEASE_NOTES:禁写调试功能、技术细节、用户无感知的改动、开发者术语。

历史沿革怎么长出来

FEATURES 里每个功能带一条「历史沿革」,靠状态流转自动生长:每次状态变更、方案调整都追加一条带日期的记录。日期读的是设备系统时间,方案没变过也要写一条「初始需求」。

METHODOLOGY 的写入原则

  • 提炼本质,同类合并,新条目标注日期,避免照搬对话原文
  • 不记技术实现细节(那是 CHANGELOG 的事),不记一次性临时决定
  • 触发时机:用户解释了「为什么这样做」、否决了方案并给出理由、表达了明确的 UI/UX 偏好、复盘时总结了经验
  • 识别到就直接写入,写完简要告知,无需每次征求许可

为什么必须放在仓库里

设计决策写在外部文档工具里也没用——AI 读不到外部文档。 放在项目仓库内的 Markdown 文件,是唯一能让它自动获取上下文的方式。

文档的位置本身就是一种架构决策。


八、审查清单

样式

  • 项目里每个控件只有一套主实现,没有并列的「新版本」
  • 颜色、圆角、间距已收成少数几档变量
  • 规则文件里写了「写新样式前先搜变量和公共组件」
  • 收敛是等价替换,视觉上看不出变化

注释与代码保护

  • 关键函数的注释包含背景、设计意图、关键约束三要素
  • 重构没有删除背景与设计意图注释
  • 删除代码前有明确声明与理由,没有静默删除
  • 没有空捕获,所有错误分支都有实质性处理

调试

  • 修 Bug 前先加日志或断点,没有猜测性修复
  • 动手前回答了链路、波及面、同类坑三问
  • 修复后声明了影响范围
  • 交付前真实接口可访问,单元测试通过

交付

  • 没有「先做简版、后续再优化」的遗留模块
  • 复杂功能的拆分决定由人做出,而非 AI 自作主张

文档

  • docs/ 下有 FEATURES、CHANGELOG、RELEASE_NOTES、METHODOLOGY 四份文档
  • CHANGELOG 时间戳来自系统时间,没有积压补写
  • RELEASE_NOTES 里没有技术细节和用户无感知的改动

清单用法:不要一次性全勾。先挑当前项目里最容易出事的那一类,只把那一类勾完,改动一次、验证一次。清单的价值在于暴露盲区,不在于制造「全部通过」的成就感。


九、约束说明

本集内容严格取材于 content-slices/pm30-m5.md 所聚合的五节课节素材,不跨集取材、不虚构源文档没有的数据、案例与引文。

几处需要说明的约束:

  1. 英文缩写处理。 口播稿中不直接念英文缩写与文件名,统一转译为中文表达(如样式表、设计变量、日志、单元测试、第三方登录、最小可用版本等),以保证非工程背景听众的听感。
  2. 文件名与代码标识符处理。 素材中的文档文件名、类名、函数名、包名不进入口播,改为「功能清单」「变更记录」「发布说明」「方法论手册」等中文指代;这些原名保留在本解读稿中,便于查阅。
  3. 数字表述。 口播稿中数字统一用中文表述(如百分之六十七、九十天、九个模块),便于语音合成时的自然朗读。
  4. 符号与表情隔离。 素材中的状态图标与对错标记不进入口播稿,本解读稿中保留以说明文档规范。
  5. 题库隔离。 本集无题库页,口播稿不含任何自测题目内容。
  6. 时长与字数门禁。 口播稿净字数控制在 4600 至 5300 之间,对应音频时长 12 至 20 分钟,均符合产线门禁。
  7. 来源标注。 本集所有素材来源为小山学堂 · 洛小山《学 AI 产品,从入门到精通》,二次演绎配音版。

十、来源

本集素材来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)

相关内容整理自开源仓库 itshen/xs_vibe_rules 中的开发规范章节,涵盖代码组织与规范、调试与日志规范、实现质量要求、版本记录与文档维护、产品方法论沉淀等主题。

二次演绎配音版,仅供学习交流使用。