一把更快的锤子
我有个朋友在做金融ToB的管理后台。去年他兴冲冲地告诉我,团队全面用上了AI编程工具,代码产出提升了三倍。
三个月后他又来找我,说交付速度反而变慢了。
我说你等等,代码写得更快了,交付怎么会更慢?
他说:以前一个需求改三个文件,现在AI一口气帮我改了十五个。改完发现权限漏了一个口子,回归测试没覆盖到,线上出了个事故,然后花了三天善后。
我突然想起了一个老笑话:给一个不会用锤子的人一把更大的锤子,他不会钉得更好,他会砸得更烂。
AI编程工具就是那把更大的锤子。问题从来不在锤子上。
The Architecture Tax
我在做Fog Creek的时候发明了一个叫“Joel Test”的东西——12个简单的是/否问题,用来判断一个软件团队的水平。今天我想发明一个新测试,叫Architecture Tax Test。
所谓Architecture Tax,就是你的系统结构每天在暗中向你征收的隐形税。每次你改一个小需求却要动七八个文件,那是结构税。每次上线前要花半天手工回归,那也是结构税。每次新同事问“这个逻辑在哪”你得想半天,那还是结构税。
问题在于,这种税是渐进的。就像温水煮青蛙,你每天多花20分钟不觉得什么,累积一年就是上千小时。
AI编程工具不仅不能减免这种税,反而会放大它。因为AI产出的速度更快,每天触发“结构税”的次数也更多了。以前你一天写一个功能,交一次税。现在你一天写三个功能,交三次税。而且AI不懂你系统里那些没写在文档里的潜规则,所以它触发的税率往往比你自己写的还高。
这就解释了我朋友的困境:AI让他写得更快了,但Architecture Tax也涨了。净效果反而是负的。
对金融系统来说,这不是效率问题,是安全问题
如果你做的是社交APP或者内容平台,Architecture Tax顶多让你慢一些。但如果你做的是金融系统,这税交不起。
金融系统有几样东西是绝对不能出错的:钱不能算错,权限不能漏洞,操作不能无法追溯。
想象一下:AI帮你快速写了一个新的退款接口,但它没注意到你们的系统里退款需要走审批流程。代码跑起来了,测试也过了(因为测试也没覆盖审批逻辑),上线后有人绕过审批直接退款。这不是bug,这是事故。
所以对金融ToB来说,架构不是“让你写得更好”的锦上添花,而是“让你不出事”的安全网。
两件事,而不是二十件事
大多数架构文章会给你列一个长长的清单:分层、DDD、微服务、事件驱动、CQRS、SAGA……看完之后你的感觉不是“我知道该怎么做了”,而是“我更迷茫了”。
我觉得金融ToB的小团队需要做的就两件事。不是二十件,是两件。
第一件:画清楚边界
你的系统里,订单是订单,流水是流水,账本是账本。这三样东西不能混在一张表里,也不能混在一个模块里。
听起来像废话对吧?但我见过太多金融系统,订单表里存着渠道状态,流水记录和业务订单纠缠不清,想做对账的时候发现数据根本对不上。
边界不仅仅是“代码放在不同的包里”。边界意味着:数据归属明确(谁能写这张表)、接口契约清晰(模块之间怎么通信)、变更范围可控(改一个模块不会把别的模块弄坏)。
当边界清晰的时候,AI的产出立刻变得安全很多。因为你可以告诉AI:“在这个模块里写代码,只通过这些接口和其他模块交互。”AI的活动范围被限定了,你的审查范围也被限定了。
第二件:建好护栏
护栏是什么?就是那些能帮你自动发现“AI搞错了”的东西。
幂等是一道护栏:同一个请求发两次,结果应该一样。这在支付场景里是生死攸关的事情——你不希望用户的钱被扣两次。做法也不复杂:每个请求带一个唯一ID,服务端维护一张去重表,先查再写。
对账是一道护栏:你的系统算出来的数字和渠道方的数字每天自动比对,有差异就报警。这意味着就算有bug偷偷溜进去了,最迟第二天你就能发现。
审计是一道护栏:每个敏感操作都记录谁做的、什么时间做的、做之前是什么样、做之后是什么样。不是为了抓人,是为了出问题的时候能快速定位。
自动化测试是一道护栏:AI写完代码之后,一键跑测试,通过了才允许合并。这是你对AI产出最基本的质量把关。
这些护栏不需要很花哨。它们需要的是存在和自动化。手动的护栏等于没有护栏,因为人会忘记、会偷懒、会侥幸。
模块化单体:不是因为简单,而是因为诚实
有一种流行的说法:微服务是“现代架构”,单体是“遗留系统”。这完全是胡说八道。
微服务解决的是一个非常具体的问题:当你的团队大到几十上百人时,如何让不同团队独立开发和部署。 如果你的团队只有五到十个人,微服务带来的好处几乎为零,但带来的成本是真实的——分布式事务、网络延迟、服务发现、独立部署流水线、运维复杂度。
模块化单体是一种更诚实的选择。它承认你是一个小团队,你的精力应该花在业务上而不是基础设施上。同时它又不放弃结构:模块之间有清晰的边界和接口,只是它们恰好运行在同一个进程里。
将来如果你真的需要把某个模块独立出去(因为它需要独立伸缩、或者有团队需要独立负责),你可以把它从模块“升级”为服务。但在此之前,你不用付那笔分布式的税。
关于Outbox模式的一个故事
我再讲一个我朋友的故事。
他们的系统需要在数据库里写入一条支付记录,同时往消息队列里发一条通知。听起来简单对吧?写完数据库,发个消息,搞定。
但有一天MQ挂了。数据库写成功了,消息没发出去。结果下游系统不知道这笔支付成功了,客户投诉了。
他们的第一反应是:加分布式事务。我说,别。分布式事务在这种场景里是用大炮打蚊子,而且大炮还可能炸到自己。
用Outbox模式就行了:在同一个数据库事务里,既写业务表,也写一张outbox表(记录“待发送的消息”)。然后用一个后台任务定期扫outbox表,把消息发到MQ。发成功了就标记为已发送。
这个方案简单到有些无聊。但它有一个非常好的性质:因为业务表和outbox表在同一个本地事务里,要么一起成功,要么一起失败。 你不需要分布式事务,不需要两阶段提交,不需要任何花哨的东西。
有时候最好的架构决策就是选择最无聊的方案。
The AI-Friendly Codebase Test
最后,我想留给你一个测试。就像Joel Test一样,这个测试用简单的是/否问题来判断你的代码库是否“AI友好”。
- 你的项目有清晰的模块目录结构吗?
- 每个模块的职责能用一句话说清楚吗?
- 模块之间是通过显式接口通信的吗?(而不是互相访问数据库表)
- 你有可以一键运行的测试套件吗?
- 你有统一的错误码和日志规范吗?
- 敏感操作有审计记录吗?
- 外部接口有幂等处理吗?
- 你有可以一键回滚的发布流程吗?
- 新功能可以用feature flag控制上线吗?
- AI在你的项目里写代码时,你有信心在10分钟内验证它的产出吗?
每个“是”得1分。8分以上,你的代码库已经是AI友好的了。5到7分,你需要花几周时间补课。4分以下,AI对你来说可能是负资产——它在帮你更快地挖坑。
这个测试的核心思想其实就一句话:AI的产出质量取决于你系统的结构质量。 如果你的系统是一个清晰的、有边界的、可验证的系统,AI就是一个极其高效的助手。如果你的系统是一团意大利面,AI就是一台更快的意大利面制造机。
给自己两到六周的时间,把分数提上去。你会发现这是你今年回报率最高的技术投资。
注:本文风格参考Joel Spolsky的技术写作风格。