AIGC标识 从维护屎山代码开始的思考:AI时代架构设计才是最重要的护城河

从维护屎山代码开始的思考:AI时代架构设计才是最重要的护城河

搞过项目的人都知道,维护老项目比自己搞新项目难度要高得多,做出来的成果还不被认可。

原因是你花费了巨量时间去揣摩别人(甚至比你菜),而不是去关注业务,这很不值得。

这话不是我编的,是每一个被屎山毒打过的人,深夜加班时心里都在默念的话。今天咱们就聊聊这个。


一、那个让我怀疑人生的老项目

我一直在维护一个中台服务——Calculation Hub,一个纯计算引擎。设计目标写得很漂亮:脱离具体业务场景。虚拟电厂、园区能管、设备运维这些上层业务,只需要注册任务、配置四段算子(取值、触发、计算、存储),剩下的计算全交给它。业务系统不嵌入计算逻辑,计算服务也不承载业务档案。

听起来很理想对吧?一个业务无关的计算中台,上层业务随便换,内核纹丝不动。我当时看到这个设计目标,差点感动得给自己鼓掌。

但两年下来,这个"业务无关"的中台,被业务语义渗透成了筛子。

打开提交记录:359 个 commit,横跨一年半,六个开发。每个人对里面的概念都有自己的理解——有人叫它 objectId,有人叫它 identityCode;有人觉得 funId 是功能定义,有人觉得它是业务键。你问他们谁对?他们自己都说不清。metadata 层早就切到了 identityCode + subjectKey,四段表的契约层还停留在 objectId + funId。设计文档里管这叫"残留的最后一处 ObjectId 入口"——翻译成人话就是:改了一半,改不下去了,剩下的先凑合着。

更早的时候,计算能力是散装的:属性公式、告警规则、数据统计、设备上下线推断,分别实现、分别存储、分别触发,各走各的门。后来统一成了四段算子模型,但每个链路——取值、触发、计算、存储——都是不同时期不同人写的,每个人都按自己对业务的理解实现了"自己的版本"。名义上是同一套抽象,实际上是四条完全独立、各自嵌入业务的链路。就像四个人合租一套房,客厅是公共的,但每个人都在自己屋里偷偷改了承重墙。

最典型的一个 bug:设备 DST2608050006 上送了功率 P,但日用电量和月用电量都不更新。排查到最后,根因是写路径上的一行代码:

entity.setIdentityCode(dto.getObjectId())

objectId 字段承载的是设备 ObjectId(一串 6a72da34e4b0f0f05ef2ec3e 这样的哈希),却被直接写进了 identity_code 列——而这一列应该存业务键 DST2608050006。于是 registry 里 Energy_Day 的 transform 没有 formula,datasources 为空,公式 P_NEW 永远不执行。日=月,月=日,数据一动不动。

一行代码,一个语义错位,线上数据静默错误。你问这行代码是谁写的?git blame 一下,大概率是某个已经离职的哥们,或者三个月前的你自己。

修这个 bug 花了多少功夫?SP1 契约重构:6 个类 11 处 setIdentityCode(...getObjectId()),7 处 getDeviceBasicInfo 误传 ObjectId,4 处 getStorageConfig 误传 ObjectId,外加 8 张表的数据回填 SQL。而这还只是第一步——后面还排着 SP2 的 Device/Product 改名、SP3 的缓存架构治理。一个 bug 修出一个重构三部曲,就问你这福气要不要。

那一刻我悟了:维护老项目难,难的不是技术,是你得先解码别人的混乱。 而这个项目里最扎心的是,写这些代码的人不是不会写,是每个人对"中台"的理解都不一样。你花在解码上的每一分钟,都不是在创造价值,是在替不同时期的不同理解还债。


二、老项目是怎么变成屎山的

那问题来了:老项目是怎么一步步变成屎山的?Calculation Hub 只是其中一个例子。

不是没人设计过。是设计的时候,压根没把"业务会变"这件事当回事。

我见过太多这样的场景了:

产品原型画成什么样,数据库就建什么样。产品经理在 Axure 里拖一个输入框,后端就加一个字段;画一个 tab 页,就建一张表。美其名曰"快速响应需求",实际上是把数据库当成了产品原型的像素级复刻。屎的形状,取决于产品经理那天画原型时喝了几杯咖啡。

需求说"订单要支持发票信息",就往订单表塞一个 JSON 字段。需求说"要记录优惠券核销",再往同一个 JSON 里塞一个数组。半年后打开线上一条订单,里面是一坨 4KB 的 JSON,套娃套了三层,没人知道哪些字段还在用、哪些是历史遗留。你以为你在做"灵活设计",其实你在做"向未来的自己拉屎"。

状态用八个布尔值表达,因为"语义清晰"。八个布尔值有 256 种组合,合法的业务状态只有 12 种,剩下 244 种全是非法状态——但代码里没有任何地方约束它们不能出现。于是线上 bug 永不绝迹,用户发邮件骂:"为什么我的订单显示已付款已取消已发货未退款?" 你回邮件说"这是系统设计如此",然后默默去改代码。

当时省下的每一分钟建模时间,后来都以"每月两次线上故障"的形式还了回来。利息高得吓人,堪比高利贷。

为什么会这样?因为没有向上抽象的思维。没有人向上问一句:这些字段在业务上究竟是什么关系?发票是不是一个独立的领域实体?订单本质上是不是只有一个状态属性?

没有人问。因为问这些问题需要抽象思维,抽象思维需要动脑子,动脑子累。照着原型建表多轻松啊——屎来伸手,屎去张口。

这就是没有架构设计的代价。但更准确地说,是没有"基于业务思考的架构设计"的代价。


三、架构设计的本质:预判变更

工程项目业务是关键。这句话我越做越觉得是真理,就像"钱不是万能的,但没有钱是万万不能的"一样真理。

很多人对架构设计有误解,觉得架构就是画漂亮的 UML 图,就是选一个时髦的框架,就是把微服务拆得越细越高级。都是扯淡。你见过哪个架构师靠画图吃饭的?画图是给领导看的,架构是给业务扛雷的。

架构设计的核心就一件事:预判后续变更。

怎么预判?按变更频率分三类,每一类对应一条原则:

高频变更——易开发(DRY)

业务里天天变的逻辑,比如价格计算、状态流转、权限规则、优惠策略。

这类变更如果散落在十几个地方,每次改需求都要全局搜索、提心吊胆,改完这处忘了那处,上线就出事故。DRY 的意义从来不是"代码写得优雅",而是让高频变更只改一处

改一处,测一处,上线一处,十分钟搞定。散落各处,改十处,测十处,上线提心吊胆一整天。同样是改一个需求,前者是日常,后者是渡劫。

中频变更——可扩展(里氏替换)

业务里隔三差五会加新类型的变更,比如支付方式、消息渠道、第三方对接、营销活动类型。

这类变更你预判不到具体是什么,但能预判"它一定会来"。里氏替换的意义是:新加一个实现类,老代码一行不用改

今天接微信支付,明天接支付宝,后天接抖音支付,都是往注册表里加一行的事。如果你当初把支付逻辑写死在订单服务里,每接一个渠道就是一次伤筋动骨,改完还要担心把老渠道搞挂。就像换灯泡,接口统一了,拧下来拧上去的事;接口不统一,你得把整个天花板拆了。

低频变更——影响可控(模块化/依赖接口)

业务里几乎不变的,比如底层存储、消息中间件、第三方 SDK、基础设施。

这类变更几年才碰一次,但一旦碰就是大手术。模块化、依赖接口的意义是:换数据库的时候,业务代码一行不用动;换消息中间件的时候,只有对接层在变。影响范围被隔离在一个模块里,而不是炸穿整个系统。

你看,这三条没有一条是"技术"问题,全是"业务"问题。

DRY 的前提是你知道什么会高频变;里氏替换的前提是你知道什么会中频变;模块化的前提是你知道什么会低频变。而知道什么会变,只能来自对业务的理解。

技术方案是术,业务判断是道。术可以学,道只能悟。


四、AI 时代:架构设计不是不重要了,是更重要了

现在说 AI。

AI 时代最根本的变化是什么?写代码的成本趋近于零了。

以前一个需求,方案想三天,代码写两周。现在方案想三天,代码让 AI 三个小时生成完。AI 把"执行方案"的成本打到了地板上,但"想清楚方案"的成本一分没降——反而更贵了。

为什么更贵了?因为以前架构烂,还有编码成本这个缓冲垫。代码写得烂,至少写得慢,屎山堆积的速度有限,你还有机会在堆到三层的时候停下来想想。

现在呢?AI 一秒钟生成 200 行代码,你审查的速度根本跟不上。烂架构 + AI = 屎山加速器。以前三年堆一座屎山,现在三个月就能堆一座,而且每一层都"看起来挺规范"——命名规范、结构清晰、注释齐全,单看每一层都挑不出毛病。就像预制菜,每一份都包装精美,凑一桌就是灾难。

我上一篇说过,AI 是放大器。能力 100 的人用 AI,是 1000;能力 10 的人用 AI,还是 10,甚至 1。架构也是一样:好架构 × AI = 生产力爆炸,烂架构 × AI = 灾难加速。

更可怕的是 AI 生成代码的特征:局部极其优秀,全局稀烂

你让 AI 写一个模块,命名规范、逻辑清晰、边界条件都处理了,单看质量比很多 junior 写得好。但十个模块拼在一起,风格不统一、接口不对接、数据流没人说得清——因为每个模块都是独立生成的,没人对"全局"负责。

就像十个装修工人分别装修了同一套房的十个房间,风格不统一,走线不对接,承重墙的位置没人对齐过。这个比喻我在上一篇《Code is cheap》里用过,用过了就用过了,好比喻不怕重复。

而"全局",就是架构。

所以 AI 时代,架构设计不是不重要了,是更重要了。以前架构的价值被编码成本稀释——反正代码要人写,架构烂一点,多花点时间总能磨出来。现在编码成本没了,架构烂,AI 会以十倍速度把烂架构变成烂系统。你连"磨"的机会都没有。

以前的技术债是"为了赶工写了烂代码"。以后的技术债是"为了赶工生成了太多没人审查的代码"。病因不同,症状一样——系统变得不可维护,而且不可维护的速度,快了十倍。


五、架构设计靠什么:业务思考,和人聊天

那架构设计靠什么?靠技术深度?靠设计模式背得熟?靠画图好看?

都不是。靠的是业务思考

但业务思考不是坐在工位上憋出来的。憋是憋不出来的,你对着需求文档憋三天,憋出来的还是需求文档。我总结下来,架构设计是这么个流程:

第一步:跟正确的人聊天,谈星星谈月亮

预判变更,你得先懂业务。懂业务,你得先和人聊天——和产品聊,和业务方聊,和用户聊,和一线运营聊。聊他们现在怎么干活,聊他们哪里痛,聊他们接下来想干什么。

注意,是"正确的人"。跟错误的人聊天,聊三天三夜也是白聊,还搭进去一顿饭钱。正确的人是谁?是那个真正在业务里摸爬滚打的人,是那个被业务痛点折磨得睡不着觉的人,是那个能说出"我们其实想干的是这个,但一直没说出来"的人。

而且聊天要"谈星星谈月亮"——不是拿着需求文档一条条对,是聊业务方的野心、恐惧、KPI,甚至他们老板的老板在想什么。变更不是凭空预判的。变更的种子早就埋在业务方的脑子里了,只是他们自己还没说清楚。你天天闷头写代码,看到的只有"现在";你走出去和人聊天,才能看到"以后"。

第二步:回到家,夜深人静时,自己思考

聊天聊完,信息是散的。业务方说了一百句话,有用的可能就三句,而且这三句他自己都没意识到。这时候你得回家,夜深人静,把今天聊的东西在脑子里过一遍。

向上抽象。把"我们要支持抖音登录"抽象成"第三方登录是一种会持续新增的认证方式";把"订单要支持部分发货"抽象成"订单状态是一个会持续演化的状态机"。业务方说的是具体的事,你要听的是抽象的模式。

这一步没人能帮你。白天你可以和人聊天,晚上只能自己面对自己。就像练内功,师傅能教你招式,但真气得自己运。

第三步:浸入骨髓的设计模式和架构思维,无招胜有招

抽象完了,怎么落地?这时候设计模式和架构思维就派上用场了。但注意,是"浸入骨髓"的——不是背概念,是它们已经长在你脑子里,遇到问题自动浮现。

就像独孤九剑,风清扬教令狐冲的时候说:你学剑招,不是为了用剑招,是为了忘掉剑招。真正的高手,出手的时候脑子里没有招式,只有对局势的判断。架构设计也一样:你学 DRY、学里氏替换、学模块化,不是为了在画图的时候一个个对照,是为了让它们成为你的本能。遇到一个需求,你不需要想"这里该用策略模式还是模板方法",你直接就知道该怎么切。

这就是无招胜有招。招式是死的,业务是活的。你把招式练到骨髓里,才能用活招式去应对活业务。

这也是为什么维护老项目那么痛苦——因为当初设计它的人,大概率没和正确的人聊过天,也没在夜深人静时向上抽象过。他对着原型建表,对着需求写代码,把每一个"现在"都固化进了系统,唯独没想过"以后"。于是"以后"的每一次变更,都变成了一次考古。


六、结语

AI 时代,写代码的门槛被 AI 踩平了,但架构设计的门槛反而更高了。因为当代码变得廉价,唯一值钱的就是"知道该写什么"。

架构设计,就是那个"知道该写什么"。

别把时间花在揣摩别人的屎山上,也别把时间花在让 AI 更快地堆屎山上。把时间花在理解业务上,花在预判变更上,花在和人聊天上,花在夜深人静时和自己对话上。

代码会过时,框架会过时,AI 也会过时。但"预判变更"的能力不会过时——因为只要业务还在变,架构设计就永远是最重要的护城河。

:::info
架构设计的本质是预判变更,预判变更的前提是理解业务,理解业务的途径是跟正确的人聊天,然后在夜深人静时向上抽象,把招式练成修为,无招胜有招。
:::

posted @ 2026-08-17 15:23  向空月  阅读(2)  评论(0)    收藏  举报