FunTester 生产代码教会我的底层设计

FunTester · 2026年04月22日 · 50 次阅读

我对底层设计的理解,不是从书本和模式里背出来的,而是在一次次维护旧代码、应对变更和收拾连锁问题的过程中慢慢长出来的。

我最初入行时并没有考虑设计

我入行之初,并没有认真考虑过底层设计。和很多工程师一样,那时我对工作的判断标准非常直接:功能能不能跑通,API 能不能返回正确结果,线上有没有报警,需求是不是按时交付。只要这几项都过关,我就会默认自己做得不错,也会下意识觉得设计这种事离我还很远,仿佛那是资深工程师、架构师或者技术博客作者才会讨论的话题,而不是一个刚把业务做出来的人应该立刻关心的事。

现在回头看,那种想法其实很常见,也不完全算错。问题在于,当时的我只看见了代码能不能工作,却没有意识到代码以后会不会变得难以修改。我很少去想类边界是否合理,依赖关系方向是否清晰,也很少去想同一段逻辑在半年后被别人接手时,会不会让人头疼。那时候我并不是故意忽略设计,而是根本还没有真正感受过设计缺失带来的代价,所以自然也不会把它放在很高的优先级上。

当真正的生产代码改变了一切

真正改变我认知的,不是某篇文章,也不是某个课程,而是一段已经在生产环境里运行多年的系统代码。那不是一个从零开始、结构还算整洁的新项目,而是一套经历过多轮需求堆叠、业务变化、人员交接之后依然在持续运转的老系统。某次我接到一个看起来几乎不值一提的需求,只是对现有后端流程做一点小小的业务规则更新。我原本以为自己很快就能定位到改动点,结果打开代码后才发现,事情远没有那么简单。

我一边往下翻,一边开始感到不安,因为一个类里同时塞进了验证逻辑、外部服务调用、数据库更新和业务规则判断,不同层次的职责被混在一起,任何一点改动都可能把整条链路一起牵动。后来我只是调整了一个很小的地方,就引发了连锁反应:修好这里,别处又坏;补上那个地方,这边又开始不稳定。最让我震动的不是系统不能用,恰恰相反,系统已经稳定运行,用户满意,指标正常,可作为开发者的我却明显感到疲惫和畏惧。那一刻,我第一次真正明白:代码可以运行,但设计仍然可能很糟糕。

糟糕的底层设计带来的隐形代价

从那之后,我开始在工作里反复注意到一些之前被自己忽略的信号。比如一个看起来不复杂的功能,开发周期却总是比预期更长;比如某些改动明明范围很小,大家却总会反复确认、迟迟不敢下手;再比如团队里会自然形成一些谁都不愿意碰的文件,仿佛那些地方一旦改动,就必然伴随着不可预测的副作用。这些现象以前我只会把它们归因于业务复杂、历史包袱重或者项目节奏紧,但后来我越来越确定,很多问题并不是复杂本身造成的,而是结构不清楚造成的。

真正隐形的代价,不是代码变丑,而是团队逐渐失去安全修改代码的能力。一个设计差的系统,最先吞掉的不是机器资源,而是工程师的认知资源。每次改动前都要靠猜,每次联调都像排雷,每次重构都像赌博。久而久之,大家就会把更多精力放在避免出错上,而不是放在提高质量上。也正因为如此,我才慢慢理解,底层设计真正解决的不是美观问题,而是演化问题。它关心的从来都不是当下这段代码看起来多高级,而是未来的我们还能不能以一个可控的成本继续改它。

为什么从模式中学习底层设计行不通

在意识到问题之后,我很自然地去找所谓的答案,而最容易接触到的答案几乎都指向 LLD、设计模式和各种原则。我读了不少文章,也看了很多视频和图示,内容通常围绕 SOLID 原则、模式分类、UML 图以及一套看起来非常完整的设计话语体系展开。那段时间,我以为自己终于找到了入口,于是开始努力把这些概念直接套进工作代码里,试图通过增加抽象层、引入接口和创建模式,让系统变得更像一份标准答案。

可惜现实很快告诉我,概念本身并不会自动转化成理解。我写过只有一个实现类的接口,做过只包了一层构造函数的工厂,也堆过一些名字很漂亮、但并没有抽象出真实业务差异的层次。表面上看,代码似乎更像设计过的样子了,可读性和可维护性却没有变好,反而更难理解。后来我才意识到,问题不在于模式没用,而在于我把模式当成了起点。设计模式并不能纠正错误的理解,反而会加剧这种错误理解。如果我们连变化发生在哪里、职责应该如何拆分都没想清楚,那么模式只会把混乱包装得更正式一些。

在真实代码库中学习

真正让我开始学会底层设计的,并不是再多记住几个模式名字,而是重新回到真实代码库,逼着自己去观察问题本身。我不再把 LLD 看成一组定义、一套术语或者几张图,而是把它理解为一种读代码和拆问题的方式。也就是说,先不急着问这里能不能上模式,而是先问这段代码到底在承担什么职责,它为什么会这么耦合,它现在的痛点究竟是什么。如果这些问题都没看明白,任何设计动作都很容易变成自我感动。

我后来给自己保留了一组几乎每次都会问的问题:这个类存在的意义是什么,它实际承担了哪些责任,为什么这个方法需要那么多上下文,哪一部分最有可能在下一次需求里发生变化。这些问题刚开始并不会立刻给出答案,但它们会逼着我从功能视角切换到结构视角。慢慢地,我发现自己读代码时不再只关心逻辑能不能跑通,而开始留意职责边界、依赖方向和变化来源。也正是这种观察方式的变化,让我第一次觉得自己不是在背设计,而是在真正理解设计。

设计应面向变化,而非面向模式

后来我逐渐形成了一个对我帮助非常大的判断方式:不要先从模式出发,而要先从变化出发。因为真正推动系统演化的,通常不是代码行数的增加,而是业务规则调整、新的系统集成、边界条件不断增加,以及规模扩展之后对稳定性和扩展性的要求变化。只要我们开始认真观察这些变化从哪里来,就会发现很多设计问题其实并不神秘,它们只是长期把不同性质的变化混在同一个地方,最后把系统拖成了一团。

一旦我开始用这种视角看代码,很多决策就自然清楚了。真正需要拆分的,不是代码最长的模块,而是变化来源最混杂的地方;真正需要保护的,不是最抽象的接口,而是那些相对稳定、值得被隔离出来的核心规则。稳定的事物要尽量保持稳定,不稳定的事物要被限制在局部范围里。换句话说,设计的目的不是为了画出一张漂亮的结构图,而是为了让变化停留在它该停留的范围内。只要这个目标明确了,很多所谓高深的设计动作反而会变得非常朴素。

微小的改进带来了最大的改变

理解了变化之后,我并没有立刻去重做整个系统,因为那通常既不现实,也很容易把事情做过头。真正起作用的,反而是那些很小、但方向正确的调整。我开始要求自己一次只处理一个方法,或者一次只处理一个类,优先把最明显的混杂职责拆开,把应该属于业务决策的东西从基础设施细节里拿出来,把那些隐藏得很深的依赖关系暴露出来。这样的调整看上去不轰轰烈烈,但它们会明显改变系统后续被修改时的手感。

举个最直接的例子,当我把决策与执行分开之后,代码并不会神奇地变高级,可一旦业务规则变化,我们就不再需要顺手去改数据库访问、外部接口调用和流程控制。这样做最宝贵的地方,不是某个类图看起来更优雅,而是下一次有人接手时,能够更快判断哪里该动、哪里不该动。它当然不完美,也绝不可能一蹴而就,但这些小改动会持续累积,直到某一天你突然发现,自己面对改动时已经没有以前那么怕了,而这种恐惧的减轻,本身就是设计开始起效的信号。

当代码开始平静下来的时候

随着这些小调整逐渐累积,我开始感受到一种很难用炫目的语言描述、但非常真实的变化。职责明确的时候,重构会更安全;依赖关系清晰的时候,测试会更容易组织;代码意图足够明确的时候,Bug 更少,而且出了问题以后,团队可以更快沿着职责边界去定位根因。这里最重要的不是某个单点技巧,而是大家不再需要靠猜测理解系统。系统开始呈现出一种稳定的秩序感,代码不再总让人担心某个意外会从看不见的地方冒出来。

我后来把这种感觉叫作平静,甚至可以说是一种略显无聊的平静。以前写代码时,我总会下意识地紧张,觉得一步走错就可能把一整片逻辑带崩;后来这种感觉明显变弱了,因为代码的边界开始清楚,变化的传播路径也更可预期。很多人谈设计时喜欢谈优雅、抽象和高级感,但对一线开发者来说,真正好的设计常常带来的不是兴奋,而是安稳。你知道这次改动可能影响哪里,也知道不太会影响哪里,这种确定性就是优秀设计最有价值的回报。

设计模式终于变得有意义了

很有意思的是,当我不再执着于套模式之后,设计模式反而开始真正有用了。因为这时候它们不再是我要硬塞进去的模板,而是在问题足够清楚之后自然浮现出来的结构。当对象创建逻辑不断扩展时,工厂模式会显得顺手;当业务规则因为不同类型而不断分化时,策略模式会很自然地出现;当我们想把通知、审计、埋点这些副作用从主流程里拆出来时,观察者模式就会开始变得有价值。

也就是说,模式真正的作用并不是替你思考,而是在你已经看清问题结构之后,提供一种更稳定的表达方式。过去我把模式当成目标,所以很容易做成形式先行;后来我把模式当成结果,反而会更克制,也更愿意接受有些地方根本不需要模式。模式不再是我要刻意添加的东西,而是我能从问题形状里认出来的东西。当这种转变发生后,我才真正觉得自己开始理解了设计,而不是只会复述设计的名词表。

底层设计的真正意义是什么

走到这个阶段,我对底层设计的理解也变得比以前简单了很多。它并不是为了写出完美无瑕的代码,也不是为了证明自己懂多少原则、多少模式,更不是为了让代码看上去高级。对我来说,底层设计并非在于编写完美无瑕的代码,而在于将代码变更带来的影响降至最低。这个定义听起来没有那么华丽,但它几乎解释了我后来所有有效的设计判断。

因为每一个设计决策,最终都会体现在未来修改成本上。好的设计会让变化被约束在局部范围里,让人知道从哪里下手、哪里需要防守、哪里不该被牵连;坏的设计则会把无关的部分绑在一起,让每次改动都不得不带着额外的风险前进。我们总会面对变化,业务不会停,系统也不会永远静止,所以设计真正的意义,不是抵抗变化,而是让系统能在变化里继续保持秩序。如果一个设计让后来的修改越来越可控,那它大概率就是有效的。

协作如何提升了我的设计思维

除了写代码本身,协作也极大地改变了我理解设计的方式。以前团队讨论我的代码时,大家常常会问为什么要这样写,这种问题背后通常意味着结构还不够自解释。后来随着边界逐渐清晰,讨论内容开始变化,大家更常说的是这个拆分说得通,这个依赖方向合理,这个职责放在这里很自然。对我来说,这种变化特别重要,因为它说明设计不只是我自己觉得顺眼,而是真的让别人更容易理解和维护。

另一个对我帮助很大的场景,是给学弟学妹或者团队新人解释代码。当我发现自己没法用通俗的语言说明一个设计选择时,这往往就是危险信号。要么这个设计过于巧妙,只对作者本人友好;要么这层抽象根本没有提供足够的价值,只是增加了理解成本。很多时候,教学和代码评审比单纯看书更能磨练设计判断,因为它们迫使我们面对一个事实:真正好的设计,不是讲起来显得厉害,而是别人能迅速理解,并在此基础上继续安全地修改。

如果底层设计让你感到不知所措

如果你现在也觉得底层设计很难,甚至有点抓不住重点,我其实非常能理解,因为我也经历过那个阶段。它看上去像是一个巨大而抽象的话题,里面塞满了原则、术语、模式和各种似乎都很重要的讨论,很容易让人误以为自己必须先把整套知识体系学完,才有资格开始做设计。但实际情况恰恰相反,设计并不是一门先学完再使用的能力,它更多是在一次次修改旧代码、一次次踩坑、一次次返工中慢慢长出来的。

如果一定要说我后来最有效的学习路径,其实非常朴素:触碰混乱的代码,打破东西,仔细地修理它们,然后继续重复这个循环。这个过程没有捷径,也不太浪漫,但它会一点点训练我们识别职责混杂、发现变化来源、判断抽象是否必要。只要你愿意持续和真实问题打交道,设计感就会慢慢形成。所以如果你现在觉得慢一点、笨一点,真的没关系,这本来就不是靠速成建立起来的能力。

底层设计真正带给我的是什么

很多人会把设计和高效率、高手感或者漂亮架构图联系在一起,但对我来说,设计最终带来的收获其实更朴素。它没有让我一夜之间写代码变得飞快,也没有让我的代码自动显得多么花哨,它真正带给我的是自信。这种自信不是那种我什么都懂的自信,而是一种更踏实的感觉:我知道自己可以在复杂系统里逐步定位问题,也知道自己有能力在不过度设计的前提下,为未来的变化留出空间。

再具体一点说,这种自信体现在三个层面。第一,我越来越相信自己可以安全地修改旧代码,而不是一想到改老系统就本能退缩;第二,我开始相信复杂性并不一定要靠硬扛,很多时候只要边界清楚,复杂问题也能被拆开理解;第三,我知道所谓面向未来的设计,并不是预判所有需求,而是在关键位置保留调整余地,不让今天的决策把明天彻底锁死。对我来说,这种稳定增长的把握感,比任何模式清单都更像真正的成长。

最后想说

现在如果再让我总结什么是好的底层设计,我大概不会用太多复杂词汇。我会说,好的设计往往并不引人注目,甚至常常让人觉得有点无聊、平淡、安静。它不像某些华丽的方案那样第一眼就显得厉害,但当你真的在代码库里工作时,你会发现这种平静特别珍贵。你改一个地方时,知道影响大概会停留在哪里;你读一段代码时,能很快分清职责;你交接给别人时,也不会总担心对方踩进隐藏陷阱里。

而一旦你真的体验过这种平静,就很难再回到过去那种每次改动都提心吊胆的状态了。因为你会明白,工程上的舒适感并不来自花哨,而来自秩序;不来自抽象层越多越好,而来自抽象刚刚好;不来自模式堆得够不够全,而来自代码是否真的顺着问题本身长出来。如果你的代码用起来越来越流畅,团队讨论越来越聚焦,改动越来越可控,那你大概率就已经走在正确的路上了。


FunTester 原创精华
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暫無回覆。
需要 登录 後方可回應,如果你還沒有帳號按這裡 注册