作者在日本的制造业公司维护PHP/VB.NET/Oracle遗留系统,几个月日常使用Claude Code后发现PreToolUse hook比CLAUDE.md规则更可靠——它能强制拦截危险shell命令(SQL写操作、rm -rf等),避免长会话下的上下文漂移。
我在日本一家中型制造企业维护内部系统:早于 Composer 出现的无框架 PHP,多年未曾重新编译的 VB.NET ClickOnce 应用,以及一个唯一文档就是其本身的共享 Oracle schema。我已经每天在这种环境里运行 Claude Code 数月,并将其推广给了同事。
现代技术栈的建议("跑一下测试就好了!")在这个世界里根本站不住脚。以下是真正有效的方法。
CLAUDE.md 规则只是建议。在上下文压力下——长会话、压缩后的历史——模型可能会偏离这些规则。而 PreToolUse hook 不会偏离:它是一个程序,在每次执行前检查每条 shell 命令,当匹配到危险模式时强制弹出人工审批提示。
经过任何数据库 CLI 的 SQL 写操作动词:INSERT/UPDATE/DELETE/DROP/TRUNCATE/ALTER,通过 mysql、psql、sqlplus、sqlcmd——包括管道导入的 .sql 文件(其内容在 hook 执行时无法看到)
rm -rf, git push --force, git reset --hard, curl | sh
Windows 上的服务重启和注册表写入
机制很简单:hook 以 JSON 格式从 stdin 读取工具调用,用正则扫描命令,返回 permissionDecision 为 "ask" 并附带原因。二十行 bash 或 PowerShell。关键设计决策:ask,而非 deny——合法的破坏性工作仍然可以发生,只是需要有人确认。
我们最惊险的事故根本与 SQL 无关。一个 Shift-JIS(CP932)源文件被一个假设 UTF-8 的工具编辑了。每个日文字符都变成了 U+FFFD——一旦保存,原字节就消失了。没有任何转换能恢复它,只有损坏前的备份才行。
如果你的遗留资产包含 Windows 代码库,很可能存在非 UTF-8 文件(CP1252 也算)。两种防御手段:
一个 PreToolUse hook,阻止对任何无法以严格 UTF-8 解码的文件的编辑,也阻止对已包含 U+FFFD(曾经损坏过——编辑会使其固化)的文件的编辑。
第一天就清点一遍:列出每个无法以严格 UTF-8 解码的源文件,然后逐个慎重决定是转换还是保留遗留编码。
在一个没有测试、没有原始作者的代码库里,"这看起来没用到"是一个陷阱。我们的规则:没人当前能理解的路径放入 frozen-paths.txt。Hook 让 Claude 可以自由读取和研究它们,但任何编辑都需要人工审批。随着调查将未知变为已知,路径会被解冻。
这颠覆了通常的动态关系。不是寄希望于 AI 处处小心,而是精确声明哪些地方必须小心,然后让机器来执行。
在遗留系统上最有价值的 Claude Code 会话产生零代码变更。我们运行分阶段的、只读的调查:
外围:资产清单、入口点(URL、main、cron)和出口点(数据库连接、文件写入、网络调用)
数据地图:代码实际读写哪些表,从哪些文件——表到代码的交叉引用
金钱路径:那几条关键流程的端到端追踪,与真实记录核对
风险登记册:还有什么仍不理解(保持冻结)、单点故障、定时炸弹
每项发现都写入 NOTES.md。只增加知识的会话是成功的会话——几周后,这个没有文档的系统重新有了文档,是作为副产品写成的。
Y2K38 就是今天的 bug:32 位 PHP 的 epoch 时间戳在 2038-01-19 之后溢出。证书过期和长期日期计算现在就会命中这个问题。通过 DateTime 处理未来日期,永远不要用 epoch 整数。
退出码 0 会骗人:遗留批处理脚本会吞掉错误。验证产物(文件存在、是新的、行数有变化),而不是退出码——尤其对无人值守作业。
会话依赖的服务:在登录时启动的进程能看到映射的网络驱动器;同一个进程作为 Windows 服务则看不到。更改启动方式会静默更改它能访问的内容。在触碰启动行为之前先枚举这些依赖。
字节定义的列:VARCHAR2(10) 按字节计会在 10 字节处截断多字节文本,而不是 10 个字符。按字节验证。
以上都不需要什么特殊的东西——hooks、一份冻结路径的文本文件,加上只读调查的纪律。如果你不想定制开发,我把我们所有的 hooks、CLAUDE.md 模板和操作手册打包成了两个小套件:一个团队治理包和一个遗留系统生存包。但上面的思路才是有价值的部分,自己花一个下午实现就够了。
关于在真正老旧的系统上运行 Claude Code 的问题欢迎提问——这是我的日常工作。