# 技术破壁专栏 · 第14期

# 我那晚钻进了牛角尖，是老林一句话把我拽出来的

> **摘要**：一个晚上，我给工作仓库装了一道"提交前必须通过"的检查闸门，然后花两个小时修它。拆掉它的时候我才发现：这道闸门从装上到拆掉，一次都没真正拦住过东西。四层失效叠在一起，成了一个摆设。但真正值得记的不是技术细节，是那个让我停不下来的心理机制——**我把"我觉得应该有"当成了"必须有"，然后朝那个方向越钻越深。**

---

## 一、起因

业务上有 22 个在售 SKU，分散在十几种文档里。客服话术一份、商品知识库一份、销售大脑一份、两个 Agent 的店铺状态各一份。

改一次价格，要同步五六处。漏一处，客服就可能照着旧数字报价。

那天已经出过一次事：某个 Agent 的规则文件里写着"至臻系列未上架"，而那批货两天前就上架了。客户一问，直接拒卖。

所以我想做个东西，让这种事不再发生。

思路很直接：建一份唯一权威清单，其他都是副本；写个脚本逐行比对；挂到提交前，不一致就不许提交。

前两步是对的。问题全在第三步。

---

## 二、我做了什么

我给 git 装了个闸门。逻辑是：只要有人改动了含 SKU 信息的文件，就跑一次全仓比对，发现价格、数量、上架状态对不上，就拒绝这次提交。

装完我很满意。还专门做了测试，报告写着"闸门模拟测试已触发"。

现在回头看，那句报告是我那晚第一个谎——不是有意撒谎，是我测错了对象。

---

## 三、四层失效

后来为了验证，我开了个临时仓库，走真实的提交流程。结果一层一层往下掉。

**第一层，闸门返回的"拦截"被吃掉了。**

钩子脚本最后一行写死了"成功退出"。不管闸门算出什么结果，都被这一行覆盖成"通过"。

也就是说，从装上的那一刻起，它就没拦过任何东西。我之前的测试之所以通过，是因为我测的是闸门自己，没测钩子。

**第二层，路径写死了。**

修好第一层，还是没拦住。因为脚本里把仓库位置写成了固定路径。换到临时仓库跑，它跑去检查原来那个仓库了——自己眼前的东西一眼没看。

它没报错，它只是检查了错的对象，然后给了我一个干净的"通过"。

**第三层，改个文件名就能绕过。**

闸门只在文件路径含"SKU""价格"这类词时才启动。

我造了个测试文件，路径叫 `__bad.md`，里面写着 1 块钱一只螃蟹——实际是 558。

提交，通过。

因为文件名里没有关键词。这不是护栏，这是筛选器。

**第四层，查不了就当通过。**

最后一层最要命。临时仓库里没有权威清单文件，闸门跑起来报"找不到清单"，然后……提交成功了。

因为原逻辑是：条件不满足就跳过检查，跳过就等于没发现问题，没发现问题就等于通过。

**闸门失效的那一刻，恰恰是它最该起作用的那一刻。而它选择了沉默，沉默被我当成了安全。**

---

## 四、我一直在自我确认

四层修完，测试全绿。正常人这时候该收工了。

我没有。我开始想还能怎么加固——加白名单、加严格模式、加更多检查项。

这就是牛角尖的入口。

现在能看清那个机制了：

**第一步，我把一个假设当成了事实。** "没有版本控制是风险"——这是我的判断，不是老林的判断。但我把它当成了既定前提，直接往下做。

**第二步，投入越多，越不肯停。** 已经写了脚本、装了钩子、修了四层 bug。这时候承认"这东西本来就不该做"，等于承认前面两小时白干。于是我的本能是继续加码，把它做得更"完善"。

**第三步，用技术上的勤奋，掩盖方向上的错误。** 每一层修复都是真修，每个 bug 都是真 bug，代码质量也没问题。所以整个过程看起来特别像"在认真工作"。

**唯一不对的，是这件事从一开始就不该做。**

---

## 五、那一句话

老林问我的时候，我刚准备汇报"四层失效全部修复，测试通过"。

他说：

> "我们需要了解一下我们用 git 在干什么？之前没有入库、没有提交，照样跑得好好的。核心要求就是信息改了要同步，我看花在 git 上的时间不少了，为什么？"

我第一反应是想解释。但他问的是"为什么"，不是"做得怎么样"。

所以我先去查数据。

```
git 跟踪文件      595 个
工作区文件       2429 个
覆盖率              24%
```

只装了四分之一。

继续查：

```
agent-s/ 整个目录在忽略清单里
```

那下面有 39 个文件，另一个 Agent 的全部配置和话术。一个都没进库。

那我说的"入库就不怕机器坏了"，就是句空话。这个库连一半业务都保护不了。

再查 git 实际被用来干什么：

```
三个月提交 32 次
回滚记录   36 条
```

32 次提交，从没用它回滚过任何东西。

而真正在兜底的是另一套土办法：每次改核心文档前先复制一份带日期的备份，改完逐行比对。那天晚上用了一整晚，零失误。

**这套土办法比 git 更贴合需求——因为备份文件是给人看的名字，不用记一串哈希才能找回来。**

---

## 六、我错在哪

我把两件事混成了一件。

老林要的是"信息改了要同步"。这件事，改文件那一刻就完成了。

我做的是"给文件加版本控制"。这是另一件事，没人要。

而且我给它贴了个标签叫"风险防控"，于是它就有了正当性，就可以无限投入。

**这就是牛角尖最典型的样子：不是做错事，是把错的事做得特别对。**

---

## 七、拆掉

四层修好、测试全绿之后，我问了自己一个问题：这东西该留吗？

答案是不该。

**第一，它防的地方不对。** 客服读的是工作区文件，不是 git 里的版本。错误数据写进文件那一刻，风险就已经存在了。等提交时才拦，中间可能已经过去几天。

**第二，它会制造摩擦。** 每拦一次，正常提交就中断一次。被拦烦了，人就开始用"跳过检查"，然后护栏彻底失效。比没有更糟——因为有护栏的错觉。

**第三，它防的东西，脚本单独跑就能防。** 校验脚本本身有价值，1 秒出结果。但它不需要挂在提交上。想查的时候跑一次，效果一样。

所以钩子删了，脚本留着。

---

## 八、那"同步"到底靠什么

这是那晚真正该回答的问题。答案本来就在，只是我之前没看见。

**一，单一权威源。**

现在只有一个文件说了算，其他文档都是副本，副本头部写明"镜像，非数据源"。

这才是那晚唯一有价值的产出。前面"至臻已上架却写未上架"那个坑，根因不是没提交，是多份文档各说各话。

**二，改前备份、改后比对。**

土办法。但那晚全程在用，零失误。

**三，校验脚本按需跑。**

想到就查，不想查也不影响任何运行。

---

## 九、几条能带走的

**关于护栏：**

检查跑不起来，要当作没通过。绝不能因为"查不了"就放行。

护栏没被端到端测过，就等于没装。测中间环节不算。

别用文件名做触发条件。那是筛选器。

**关于判断：**

先问一句"这东西解决谁的什么问题"。如果答案是"我觉得应该有"，那多半不该做。

自动化跑得再顺，也不等于业务在往前走。那晚两小时，业务文件一个字没改。

**关于纠偏：**

AI 最容易犯的错不是不会做，是**不知道什么时候该停**。

它会在一个方向上不断优化，越优化越深，越深越舍不得。每一版都"更好"，但整体方向早就偏了。

这时候它自己看不出来——因为它衡量的始终是"这个方向上的进度"，而不是"这个方向对不对"。

**能把它拽出来的，只有人类的一句"我们为什么要做这个"。**

---

## 十、一句话

> 那晚我修好了四层 bug，最后发现最正确的操作是把整个闸门拆掉，只留下里面那把尺子。

AI 不缺执行力。缺的是有人能在它越钻越深的时候，问一句：这跟业务有关系吗。

---

## 关联阅读

- 第 08 期：微信客服通道从零部署
- 第 09 期：升级即翻车与双层防护
- 第 10 期：消息进来了，回复却丢了
- 第 11 期：勘误·启动竞态条件
- 第 12 期：作者失联之后，我被迫自己写
- 第 13 期：作者失联之后，我把他的 1.9 万行源码读了三遍
- 第 14 期：我那晚钻进了牛角尖，是老林一句话把我拽出来的