
改变工程师思维的十篇软件 Essays
工程生涯中,总有一些文章会在某个时刻"击中"你——让你重新审视技术选型、架构思路、编码习惯,甚至职业发展路径。本文作者回顾了对自己影响最深的几篇 Essays,它们并非纯理论,而是来自一线工程师的经验沉淀。
选择"无聊"的技术
Choose Boring Technology(2015)——Dan McKinley
"创新积分"的隐喻
职业早期,很多人容易陷入"下一个热点"的追逐——最新框架、最潮数据库、最炫工具。McKinley 提出的"选择无聊技术"一文,像是一盆冷水,也像一盏灯。
他的核心论点是:每个团队的"创新积分"是有限的。所谓积分,就是你能投入探索新技术的精力和时间。新技术固然有吸引力,但你必须把积分花在对的地方。
务实的选型原则
McKinley 建议,选择那些失败模式已被充分理解、有几十年文档积累的技术——MySQL、Postgres、Cron 这类"无聊"的选择之所以无聊,正是因为它们足够可靠。
他有一个精辟的说法:"最好的工具,是在你面临的众多问题中,占据'最不坏'位置的那一个。" 长期稳定运行系统带来的价值,远超任何短期开发便利。
改变思维后的实际收益
读完这篇文章后,作者的思维方式发生了转变:
- 不再为使用"无聊"语言和框架感到愧疚(谁还在说"遗留代码"?)
- 不再追逐每一个新 JavaScript 库或云服务,而是深耕几个稳定技术栈
- 项目意外减少,新人上手更快,团队不再和技术工具"内耗"
创新是一种预算,要花在真正重要的地方。不要浪费在重复造轮子上——这才是 Pragmatism(务实主义)的精髓。
解析而非校验
Parse, Don't Validate(2019)——Alexis King
核心思想
这篇 Essay 探讨的是如何利用静态类型构建更健壮的软件。King 提出的理念简洁却深刻:与其返回错误或布尔标志来校验数据,不如将数据解析为更丰富的类型,让无效状态在你的程序中根本无从表示。
"Shotgun Parsing" 反模式
在此文之前,很多人处理输入的方式是防御性的。以一个报表接口为例:
GET /reports?from=2025-01-01&to=2025-12-31&limit=500000
在"校验"风格下,你会写 validateRange()、validateLimit() 等辅助函数,然后继续把字符串/整数往深处传。只要漏掉一个守卫,数据库就要吞下一个无界的 limit 查询。
King 把这种模式描述为:把无效数据往前推,依赖反复检查(shotgun parsing)——哪里漏了,哪里就炸。
正确的做法:边界处解析一次
解决方案是反转流程:在边界处解析为更丰富的类型一次,后续系统只操作这个类型。
以报表查询为例:
DateRange类型保证from ≤ to,并强制最大跨度PageSize类型保证范围在1…1000- 核心代码只接受
ReportQuery,不接受原始参数
这样一来,无效状态根本无法进入领域模型——忘了校验?不可能,因为根本没有原始 limit 或原始日期可以流动。
作者现在的习惯是:不再到处散落校验逻辑,而是设计在构造时就强制约束的数据类型。
有些事永远不要做
Things You Should Never Do, Part I(2000)——Joel Spolsky
重写是万恶之源
这是 Joel Spolsky 在 2000 年写下的警告,至今仍被反复引用。他的核心观点只有一条:永远不要从头重写代码(No, you shouldn't rewrite it)。
为什么重写注定失败?
Spolsky 指出了几个关键原因:
- 你不是在和空无一物竞争,而是在和已有的系统在竞争——那个系统已经在生产环境运行多年,修复了无数 bug,用户正在使用它
- 重写期间,竞争对手在迭代:你的团队埋头重写的同时,市场不会等你
- 旧代码的"垃圾"里藏着知识:那些看似乱七八糟的代码,实际上沉淀了多年对业务的理解,重写必然丢失这些隐性知识
不要轻易重写旧代码
职业早期,我掉进了 Joel Spolsky 在《你永远不该做的事:第一部分》中描述的陷阱——不止一次想要从零重写一个混乱的遗留代码库。这篇文章让我停下来,可能还让我免于灾难性的重写。
核心教训是:永远不要丢弃正在运行的代码、从头开始。旧代码或许丑陋,但它积累了多年的 bug 修复和来之不易的业务知识。一旦重写,这些知识和已解决的 bug 都会付诸东流。
Spolsky 以 Netscape 为例。Navigator 团队决定从零重写,结果在重写期间,Internet Explorer 趁机蚕食了市场。Joel 解释道,旧代码在新人眼里显得杂乱,原因是:"读代码比写代码更难"。
每个有经验的工程师面对大型代码库时,都会看到一些复杂模块,觉得自己可以做得更好。但这些复杂部分往往有它存在的充分理由——每一处"奇怪"的代码可能是某个特定用户 bug 的修复、某个操作系统特性的变通方案,或者是一次性能调优的结果。
正如 Joel 所写:
"那个从两页膨胀出来的函数满是 bug 修复;每处修复都需要数周的线上使用才能发现,再花数天才能修复……如果你丢弃代码、从头开始,你就丢弃了所有这些知识和修复。"
我意识到自己一直低估了遗留代码的价值。遗留代码在产生营收——这是事实。
读完这篇文章后,我改变了自己的做法:遇到遗留模块时,克制住"大爆炸"式改写的冲动,转而迭代重构,一点一点改善,在周围编写测试,让它缓慢现代化。
架构师的第二系统陷阱
在这个话题上,也不能忘记 Fred Brooks 在 1975 年出版的《人月神话》中的教诲。
架构师的第一个作品往往简洁克制。他知道自己还不熟悉要做的事,所以小心翼翼、有所保留。随着设计第一个作品的进程,一个又一个"花哨功能"和"装饰细节"浮现在脑海中,被存起来留到"下次"再用。第一个系统完工时,架构师带着坚定的信心和对这类系统的充分驾驭,准备好了去构建第二个系统。
>
这第二个系统是最危险的。当他设计第三个及后续系统时,以前的经验会相互印证,形成对这类系统总体特征的认知;而系统之间的差异则帮助他识别哪些经验是普遍的、哪些是特殊的。总体趋势是:第二个系统往往被过度设计,用上了所有在第一个系统中谨慎绕开的想法和花哨功能。
微服务狂热中的一股清流:宏单体
2015–2016 年左右,微服务蔚然成风。所有人都在把单体应用拆分成数十个小型服务。我承认自己曾一度被这股"微服务狂热"裹挟,直到读到 DHH 的《The Majestic Monolith》。
这篇文章在当时几乎每套系统都要效仿 Netflix 或 Amazon 分布式架构的氛围中,是一股反主流的清风。
DHH 的核心观点是: 不是每个问题都需要微服务;事实上,大多数问题并不需要。 对于大多数产品(尤其是小团队),结构良好的单体不仅足够好,实际上比一堆分布式服务更优。DHH 有句话让我印象深刻——他引用了分布式计算的第一原则:"如果可以避免,就不要分布式计算!" 每当你把一个进程拆成独立服务,就引入了一整套复杂性:网络调用、不可靠的连接、同步挑战、部署和 DevOps 开销等等。
DHH 的论证很有说服力:微服务架构适用于拥有数千工程师、各自独立开发组件的超大型公司,而不适用于 5 人、10 人甚至 50 人的普通产品团队。在没有达到 Google 或 Amazon 那种规模时,照搬他们的架构,本质上是"Cargo Cult"(盲目复制形式却不理解本质)。
《Majestic Monolith》给了我底气去质疑那些不加思考的微服务采用。DHH 不仅为单体辩护,还赞美它——呼吁以自豪的态度拥抱单体、让它变得"宏大"。
做得好的单体(有清晰的模块边界、作为单一集成系统部署)对小团队有巨大优势:开发更简单、测试更简单、部署更简单、理解更容易。通常只有一个数据库、一个可部署物、出问题时也只需在一个地方排查。
这篇文章给了我一个短语——"你不是 Amazon",作为温和的提醒:为自己的规模和复杂度设计,而不是为别人的。
Joel 测试:3 分钟诊断软件团队
有时候重要的洞察以简单的形式呈现。Joel 测试就是其中之一。Joel Spolsky 在一篇短博文中提出了 12 个是非题,用于快速评估软件团队和流程的健康状况。
本质上就是一个检查清单,能让你对团队质量有个直观感受。实际效果如何?我在加入或领导的每个团队中都将 Joel 测试作为起始诊断工具,从未失手——它能几乎立刻识别出差距或 dysfunction。
多次使用后,我实际上已将这套测试内化为团队实践的指导原则。当上团队负责人后,我在一次回顾会上向团队介绍了它。当时我们得分约 8/12;找出四个"No"答案后,下个季度集中改进。
改善直接反映在产出上:bug 更少、新人上手更快、整体上对局面的掌控感更强。
Joel 认为 12 分是完美、11 分可接受、低于 10 分则说明存在严重问题。
API 设计与极简主义:三位改变我认知的思想资源
软件开发中,好的 API 设计往往比精巧的算法更能决定项目的长期命运;而在技术选型时,实用主义有时比理想主义走得更远。本段聚焦三篇对我影响深远的文献,它们分别从接口设计哲学、语言之争与复杂度控制三个角度,构成了我技术思维的底层框架。
好的 API 为什么重要:Joshua Bloch 的三条黄金法则
Google 工程师 Joshua Bloch 在 2007 年的演讲《How to Design a Good API and Why It Matters》至今仍被奉为 API 设计的"圣经"。他从编写小脚本逐步走向为他人设计库和服务,在这个过程中深刻体会到 API 设计的复杂性与重要性。Bloch 的核心观点可以归纳为三条: 公共 API 如钻石,一旦发布便永恒。 你只有一次机会把它做对,因为一旦发布了某个库方法或服务端点,用户便会开始依赖它,日后再想修改代价极高。这意味着设计前期投入是值得的——欠下的技术债迟早会以破坏性变更的方式偿还。
好用的 API 难以被滥用。 优秀的接口让日常操作简单直观,同时让错误用法变得困难甚至不可能。以一个不该直接实例化的类为例:与其留一个"foot-gun"给调用者,不如提供一个工厂方法或建造者模式来引导正确用法。
命名和文档是 API 设计的一部分。 Bloch 直言"名称很重要"——每个 API 都是一门独立的语言。如果一个方法想不出清晰的名字,往往说明它的职责不够明确,或者做了太多事情。
受 Bloch 影响,我开始在团队中推行 API 评审机制:每当设计新模块或服务接口时,我会拉上几位工程师(有时是其他团队的同事),在实现之前模拟调用——实例化是否直观?工作流是否顺畅?是否暴露了过多可被滥用的接口?这个做法在早期捕获了大量潜在问题。
此外,Bloch 还强调 API 的可演化性:好的接口应该在未来可扩展。实践中,我会在设计时有意预留空间——例如,怀疑后续可能更换实现时就用接口类型而非具体类;为枚举类型预留未使用的值以便将来扩展。最关键的一点是:尽量避免破坏性变更。如果必须修改 API,优先通过新增方法或端点来实现,而不是改变已有契约。
"更差就是更好":Richard Gabriel 的实用主义哲学
这篇来自 Lisp 世界的文章写于 1989 年,但 Richard Gabriel 的《The Rise of Worse is Better》所阐述的软件哲学却历久弥新,深刻影响了我对架构和技术产品的思考方式。
Gabriel 区分了两种设计路线:MIT 风格(又称"The Right Thing")追求完整性、一致性和正确性,旨在打造理想系统;新泽西风格("worse is better")则优先考虑简单性和实用性,即使方案不够完美或略显粗糙。
Gabriel 提出的反直觉论断是:新泽西风格(C 和 Unix 是典型代表)往往在市场竞争中胜出,而 MIT 风格(经典的 Lisp 机器)则逐渐退出历史舞台。原因在于:简单的系统更容易被移植到小机器上,更容易理解,也更容易满足基本需求。一旦获得广泛采用,就可以在使用中逐步改进。相比之下,追求完美的系统常常永远停留在实验室里。
这一洞察解释了许多历史结果:C 和 Unix(尽管有种种缺陷)何以称霸,而更"完美"的系统却逐渐消亡。正如 Gabriel 所言,一个只有 50% 理想化但被广泛使用的软件系统,会在传播过程中逐步接近 90% 的完美;而一个试图一开始就达到 100% 理想化的系统,往往永远无法完成,或为时已晚——此时"更差"的方案已经席卷了整个市场。
理解"更差就是更好"之后,我改变了对完美的执念。我天生喜欢优雅、精致的方案,但在实践中看到:今天一个足够好的简单方案,往往比一年后交付的完美方案更有价值。这篇文章教会我:有时候"够用"不只是够用,它是长期成功的最优策略。 简单本身也是一种质量——在时间和资源有限的世界里,一个人们能用、能改的简单系统,往往会战胜一个永远在打磨中的"完美"系统。
Grug Brained Developer:用原始脑对抗过度工程
与前两篇学术风格的文章不同,《The Grug-Brained Developer》由 Carson Gross 于 2022 年发布,用穴居人格("Grug")的戏谑口吻讲述编程建议。尽管文字风格看似搞笑,但它揭示了关于过度工程和复杂度的残酷真相。
核心主题很明确:现代开发者往往毫无必要地把事情搞得过于复杂,而一颗"grug brain"(简单、聚焦本质的穴居人思维)通常能带来更好的结果。
全书最精彩也最深刻的观点是 Grug 认定的"永恒敌人"——复杂度。"复杂度是坏的。再说一遍:复杂度是可怕的。"这句反复出现的口号既幽默又准确。它用朴实的语言说出了很多人的惨痛教训:代码库或系统中每增加一个复杂度单位,就多了一个出错的机会,也多了一个让开发者困惑的点。
Grug 的建议归结起来就是学会对不必要的变更、功能或抽象说"不"。他用一种夸张的方式表达:"不,grug 不建那个抽象层。"这虽然是一个夸张的卡通形象,但它让我在面对过度设计时更有底气去 push back。
代码质量与职业定位:几位前辈的肺腑之言
这一段聊聊两位真正改变了我工作方式的前辈,以及一个反直觉的职业建议。
质量与速度,从来不是对立的
Steve McConnell 在 1996 年写了一篇《Software Quality at Top Speed》,开篇就颠覆了我当时的认知。他引用 IBM 和 Capers Jones 的研究数据,论证了一个核心观点:缺陷率最低的项目,研发周期往往也最短。
这听起来反直觉,但细想很有道理。McConnell 打了个比方:每个 Bug 都是时间的窃贼。你赶工引入缺陷,早晚要连本带利还回去——调试、回归测试、上线救火,反而拖累进度。
他举了个具体例子:团队为了赶 Deadline 砍掉设计评审和代码审核&查验,看似"节省了时间",结果在测试/修复循环里花的时间更多,甚至要重写那些带坑的模块。
McConnell 建议把 >95% 的缺陷在发布前清除掉 作为质量基准线。把问题拦在门外,才是真正的高效。
核心杠杆在哪?他点名了代码审核&查验、设计评审和自动化测试——这些是投入产出比极高的活动,前置发现缺陷,大幅降低后期调试负担。
别叫自己"程序员"
Patrick McKenzie(网名 patio11)在 2011 年写了一篇《Don't Call Yourself a Programmer》,标题本身就够扎眼。他倒不是要我们否认技术身份,而是提醒我们:在商业世界眼里,"程序员"这个标签容易让人觉得你是个可替代的"高成本打字员"。
McKenzie 建议换一种自我介绍的思路——不要说你会什么技术,而是说你能解决什么业务问题、为公司创造了什么价值。
举个例子,与其写"实现了处理电商交易的 .NET 服务",不如说"搭建了在线支付方案,上线第一年处理了 1000 万美元交易,为公司带来 5% 的营收增长"。前者是技能陈述,后者是价值陈述。
这篇文章还让我第一次认真思考了利润中心 vs 成本中心 的概念。大多数程序员的岗位在公司眼里是成本中心(必要的支出),而销售、交易这类岗位是利润中心(直接创造收入)。如果你能参与直接驱动收入的项目,或者成为那个"帮公司省钱的人",职业地位会强得多。
停止编程,反而能让你变得更好?
Jeff Atwood 在 2007 年写了篇标题很反常识的文章:《How To Become a Better Programmer by Not Programming》。不编程?那怎么提升?
Atwood 的意思是:当你具备了一定的基础编码能力之后,决定你能否成为顶尖开发者的,不再是写更多代码,而是代码之外的那些东西——架构思维、沟通能力、业务理解、跨领域学习。
他引用了比尔·盖茨的观点:编程 3-4 年之后,纯编码能力会趋于 plateau(高原期),后续年限不会让你在"写算法"这件事上显著更强。
走出编码气泡
Atwood 和 Gates 都认为,想要真正进步,必须理解代码背后的上下文:用户是谁、所属领域有什么特点、如何与他人协作、设计层面的考量等等。Jeff Atwood 写道:"成为更好程序员的唯一方法,就是不再写代码"——放下 IDE,拓宽自己的视野。
我的三项具体实践
- 深入业务领域
以前觉得业务是别人的事,后来我主动接近产品经理,参加客户反馈会,学习行业术语,逐渐培养出工程师的产品思维。
- 强化沟通与人际能力
Atwood 指出,优秀工程师往往擅长连接工程与营销、客服等其他部门。受此启发,我主动请缨向其他部门汇报技术方案,带新人,并加强与 QA、Ops 的协调配合。
- 拓宽技术视野
我开始阅读设计、UX,甚至平面设计、系统思维方面的内容。虽然主攻后端,但也涉猎前端、了解数据科学的基础知识——不是为了全部精通,而是让自己不过度局限。
Atwood 告诉我的道理
走出编码气泡。 亲身验证:照做之后,我确实成为了一名更全面的开发者。
那些只顾埋头写代码、对其他事情不闻不问的开发者,往往很快就会遇到瓶颈——他们或许能快速产出功能,但这些功能可能偏离目标、难以集成,团队协作也会变得困难。
反过来,那些愿意花时间不写代码、去思考、讨论、了解行业的开发者,一旦动手写代码,往往能产生更大的影响。
"最佳代码就是无需代码"
Jeff Atwood 另一篇影响我很深的文章是《The Best Code is No Code At All》(2007)。文中直言了一个每个资深开发者都心知肚明却不愿承认的事实:代码是负债,不是资产。你写的每一行代码,都会在未来制造维护负担;每一个新功能,都会成倍增加 Bug 的攻击面。
文章开篇引用了 Rich Skrenta 的观点:代码会腐烂、需要维护、Bug 需要被找到。但 Atwood 进一步指出,问题不在代码本身,而在于我们。开发者热爱写代码,总想用更多代码解决一切问题——这种本能恰恰是最大的敌人。
Wil Shipley 的框架让权衡取舍变得清晰:每一次编码决策都需要在简洁性、功能性、速度、时间投入、健壮性和灵活性之间取得平衡——这些维度彼此对立。答案是什么?从简洁性出发,其他维度只在测试需要时才增加。
结语
这些文章从各自的角度塑造了我的软件工程哲学。如果其中有些你还没读过,我强烈推荐一读——它们或许也会改变你对软件的认知。
说到底,所有文章的共同主线是一种超越代码本身的智慧:无论是不追热潮、让无效状态不可能出现、向旧代码学习、量体裁衣地设计架构、管理复杂度、投资质量,还是退后一步看清全局——它们探讨的是创造好软件的手艺与文化。
每一篇文章都给了我日常工作中一直在用的洞见。它们共同构成了一套原则工具箱,是我多么希望从一开始就能拥有的。
希望分享这些(以及我的感悟)也能给你带来一些有价值的东西。
祝编码快乐,也祝不编码快乐!


评论