乐于分享
好东西不私藏

源代码就是设计文档(续篇!)——来自2005年的对线

源代码就是设计文档(续篇!)——来自2005年的对线

源代码就是设计文档(续篇)——来自2005年的对线

原文《What Is Software Design: 13 Years Later》,作者 Jack W. Reeves,于2005年2月在“DeveloperDotStar.com”发表。这老哥1992年写了那篇神文后,13年后又亲自下场,逐条怼反驳言论。

经常有人问我,写完那篇《What Is Software Design》之后有没有写续集啊?我的回答基本是:“没,真没写。”但我得把话挑明了:没写续集,可不是因为我把这事儿忘了,更不是因为我向现实低头改主意了!容我解释解释当年发生了啥。

当年文章发表之后,我心里其实暗爽,甚至板着指头狂热地期待着能有哪位所谓的行业“权威专家”跳出来写篇反驳文章,跟我大战三百回合。因为我写这文章的初衷,就是想给死气沉沉的软件圈来一剂猛药,刺激大伙去讨论开发流程到底有多烂。结果呢?啥也没发生。

当时既没有读者给编辑部写信,也没人直接找我线下交流。更倒霉的是,发布我文章的那个《C++ Journal》杂志没过多久就倒闭了。我当时心想:得,这文章估计已经掉进赛博垃圾填埋场,被历史彻底遗忘了,于是我就继续去工地搬砖了。结果直到 1997 还是 1998 年(足足过了五六年!),我突然收到一封邮件,是刚接手《C++ Report》主编的鲍勃大叔发来的。他告诉我:“老铁,你知道吗?在 Ward Cunningham 搞的那个 c2.com 网站(世界上第一个 Wiki 网站,敏捷开发和设计模式的圣地)上,大伙专门给你建了个专属页面在疯狂对线呢!” 这是我第一次知道,原来除了我亲手发过复印件的几个人之外,这世界上居然真的有人读了我的文章!

Tip

  • • 最怕空气突然安静——作者:来战! 整个行业:(已读不回)。
  • • Uncle Bob:《敏捷宣言》签署人之一,SOLID原则之父,《代码整洁之道》、《整洁架构》作者。
  • • Ward Cunningham:《敏捷宣言》签署人之一,Wiki 发明者,极限编程(XP)推手。

我知道大伙在 Wiki 和新闻组里疯狂讨论我,但我故意装死不发言。原因很简单:第一,我当时忙着干别的事;第二,很多支持我观点的兄弟(比如 Michael Feathers)战力爆表,代打完全没问题;第三,也是最关键的——那些反对我的人,他们喷的观点,跟我过去15年里见过的发言一模一样,毫无长进!(别忘了,我在13年前写这文章之前,想法就已经在脑子里憋了10年了)。

跟那帮顽固不化的铁脑壳解释「代码就是设计图纸」,就像去跟一个坚信「全世界只有英语」的傲慢英国佬解释法语是一门独立语言一样! 那个英国佬只会觉得:“噢,法语啊,那不就是带着法国口音、语法写错了的英语变体吗?”
不管你用多么严谨的逻辑去证明,他们都会自动用自己那套贫瘠的固有观念去过滤你的话,要么觉得你在放屁,要么用高高在上的姿态来指点你。最让人绝望的是,我见过无数个靠「直接在代码里做设计」大获成功的项目,但连项目里的人自己都不肯承认这个现实。当时我的悲观程度已经拉满——觉得这行压根没救了,毁灭吧,赶紧的,累了。

虽然我现在依然觉得这行挺没救的,但我寻思着,是时候由我本人亲自下场来防卫家园了,总让别的大佬替我打架也不是个事儿。所以,接下来,我要把你们这十几年来针对《What Is Software Design?》提出的最常见的质疑,一个一个按在地上疯狂摩擦!

Tip

  • • Michael Feathers:《修改代码的艺术》(Working Effectively with Legacy Code)作者,CppunitJUnit的C++移植版)原作者。

1. 经典逻辑谬误:“因为你是牛马,所以你写的不叫图纸”

最常见的批评用一句话概括就是:“如果源码是设计,那码农就是设计师;但码农显然不是设计师,所以源码不可能是设计。” 虽然没人敢在大庭广众下说得这么直白,但把他们的屁话翻译过来就是这个意思。
这在逻辑学上叫“循环论证(Begging the Question)”。先预设「写代码=工厂流水线拧螺丝」这个前提,然后跑来说:“你的假设(源码就是设计)跟我的假设(码农是流水线装配工)冲突了,因为我是对的,所以你肯定是错的!”

可能有人会反水一击:“你不也是先入为主,强行假设源码就是设计吗?”——对,我当年一上来就是这么假设的! 13 年前文章的开头就写得清清楚楚:“本文假设源码就是软件设计,然后去推导这个假设会带来什么后果。我可能无法用数学公式向你百分之百‘证明’这个视角是绝对真理,但我希望能证明它能解释软件行业里发生的一切客观事实……
我压根不想跟你们在“什么才叫设计”的字面定义上抠字眼。我的核心逻辑是:我的假设能完美解释为什么软件行业有超过 100% 的返工率、为什么文档先行会破产、为什么测试其实是设计验证!

Tip

  • • 管理层:“程序员怎么可能是设计师?设计师中午吃沙拉,程序员中午吃黄焖鸡米饭,这能一样吗?!”
  • • 奥卡姆剃刀——哪个假设能解释现实,哪个假设就是真理。承认“写代码本身就是动态设计”,才能真正用科学的流程去管理它。

2. 把「创作过程」和「创作产物」混为一谈

时代变了,到了 2005 年,敏捷开发(Agile)和极限编程(XP)开始流行。项目经理们也终于开窍了,不再死板地把程序员当成工厂里拧螺丝的计件工人。但是!这并不代表他们愿意接受「代码即设计」的真理。 他们现在的逻辑变成了:我知道你们程序员很有创造力,但你们不写文档,直接在代码里做设计,这不是「无头苍蝇一把梭」吗? 例如 Wiki 页面上的一条典型留言:

至于说什么把整套设计流程扔掉——不画图、不写文档——直接在代码里做设计……哈 哈哈哈哈哈哈哈哈 你不是当真吧 哈哈哈哈哈哈哈哈哈 😀

我看到这段话当场高血压就犯了:我是真搞不懂,明明智商正常的一帮人,死活不肯分清「设计的过程(Process)」和「设计的产物(Product)」这两码事!
但凡高中毕业的人都能明白:写一篇作文的「过程」和写出来的那篇「作文」,能是一回事吗?!
更别说上过大学的了——那帮人总该知道“条条大路通罗马,同一个正确答案,可以有一百种不同的解题过程”吧?!

Tip

很多人一听到“代码就是设计”,就以为作者是让他们“别想了,直接盲写”。
但作者的意思是:我思考了,也讨论了,甚至画图了(设计过程),但最后能代表我这些思考成果的、唯一的、高精度的最终图纸,不是别的,就是代码本身(设计产物)!

这帮人搁那儿死缠烂打,非说我的“源码即设计”理论就是在教唆大家“别做设计了,一把梭直接写代码吧”。我再次重申,我从来没说过这种话!13 年前白纸黑字写得清清楚楚:

我们太需要各个层级的好设计了!尤其是顶层设计,必须得硬!前期设计越牛逼,后期细节设计就越省事儿。设计师们,有啥趁手的家伙就尽管用!结构图、Booch图、状态表、过程设计语言……只要对你有用,别客气,只管上!

搁今天,如果让我重新组织语言,我会换个说法。我会说——好的架构(顶层设计)、好的抽象(类设计)、好的实现(底层设计)。另外还得补一句:大家可以用 UML 类图或者 CRC 卡片作为寻找最优解的脑暴工具。但是!有一句话我绝对不会后退半步!

我们必须记住,这些工具和符号根本不是软件设计。最终,我们必须用某种编程语言去创建真正的软件设计。因此,在推导设计的时候,别害怕去写代码!

这就是最底层的真理。我不是反对你“做设计”,不管你喜欢用什么姿势去开始你的设计流程(画图、开会、抽签都随你),只有一条铁律:在你的代码没写完、没通过测试之前,你的设计流程就根本没有完成!

Tip

  • • 2005年,UML 正处于它的全盛时期,IBM正疯狂推行 RUP(Rational Unified Process)流程,UML 是当时企业级软件开发的绝对硬通货。今天,“代码即设计”的理念彻底胜利,极少有团队会去维护一套“与代码完全对齐”的完整 UML 模型了。大家发现写完代码再去维护几十张复杂的 UML 图纯粹是浪费生命,而“通过 UML 生成代码”也被证明生成的全是难以维护的垃圾骨架。
  • • CRC 卡片早在 1989 年就被提出来了,它是面向对象刚兴起时,大家拿来做“头脑风暴”和面向对象思维训练的纸质工具。今天这种思想被“事件风暴(Event Storming)”以及“用户故事卡片(User Story Cards)”继承了。

我觉得一个人把脚搁在办公桌上、死死盯着天花板看,他也可能是在非常严肃地做设计! 这跟那帮用着昂贵的 Rational ROSE 软件搁那儿画图的人没有任何区别!我当然知道干活前要先动脑子,但每个人思考的方式不一样啊:有人喜欢铅笔白纸,有人喜欢白板,有人喜欢找人聊天脑暴,有人喜欢清静,有人喜欢 UML,有人喜欢卡片。

你怎么思考我不管,但如果有人非要把这些思考过程中的“中间产物”当成独立的产品来考核、卡进度,那我就要骂娘了!核心只有代码!如果最后写出了牛逼的代码,谁在乎你当年是怎么想出来的?如果最后写出来的代码是依托答辩,那你之前逼着大伙做了那么多花里胡哨的垃圾流程,到底有啥用呢?!

Tip

Rational ROSE:当年 IBM 旗下的顶级 UML 建模工具,天价且臃肿,现在坟头草两米高了。它的继任者 Rational Software Architect 苟到了2024年,但结果一样——被IBM亲手送进ICU。

我也知道,在这个行业里,混久了都能见到这种场景——有人坐下来就把脑子里第一个念头写成代码,不带半点犹豫。后来发现思路错了,但因为在里面投入了太多的血汗,舍不得删掉重写,最后把项目拖死。我承认,写代码前先动动脑子,可以少走很多弯路。

但是,只要在那种严格遵守“不写完/不评审完/不批准完设计文档,就绝对不准写一行代码”的传统老古董项目里待过的人,心里都清楚:大伙浪费了巨量时间去卷文档,结果这些文档在代码真正开敲后的两三天内,就彻底变成历史垃圾了!既然注定要变成废纸,那一开始折腾个什么劲呢?

有些人可能会跳出来当理中客:“那我们找个「平衡点」嘛,设计做‘刚刚好’就行。” 根本没有这种「刚刚好」的东西!因为验证软件设计的唯一方法就是把它编译出来并进行测试!这里没有什么银弹,也没有什么所谓“正确的设计姿势”。有时候,你提前想一小时、一天甚至一星期,确实能在写代码时省点力气;但另一些时候,你坐在那儿抓破头皮想了半天,还不如直接上去写代码测个 5 分钟,测试结果暴露出的问题,你坐在那儿想一辈子也想不到! 我们能做的,就是根据当下的现实情况尽力而为,然后通过写代码去不断修正它。

最后补充一句:我也没说过“唯一需要的文档就是源代码”!我在文章里写得明明白白——

辅助文档跟硬件工程里的说明书一样重要。

源码确实是主设计图纸,但这不代表它是唯一的文档。

Tip

流程虽然让我们晚了半年上线,但至少我们留下了一堆谁也不看的精美废纸啊!

2.5 经典幻想:人不行,流程来凑?

我实在忍不住要讲一个支线话题,聊聊在讨论敏捷和极限编程时,经常有人抛出的一个终极杠点:“那那些水平不行的「低配码农」(Less Able Programmer)怎么办呢?” 这帮人的逻辑是:只有最顶尖的大佬才能边设计边编码。为了照顾大部队的平庸,我们必须搞出前面提到的那些复杂的中间设计步骤和文档,用来弥补普通程序员在经验和天赋上的不足。

这就像在问“如果医生水平太菜,我们该怎么办”一样!虽然当医生和写软件不是一回事,但你们品品:医生看病很多时候也是套路化、流程化的(“吃两片阿司匹林,明天再来找我”)。但即便如此,医学界依然死死咬住最高的智商要求、最严苛的学历教育和最长的时间经验,才允许一个人管自己叫“医学博士(MD)”并拿起手术刀!一句话:我们当病人的,是要医生真懂得自己在干嘛!

你们就是在妄想用“流程”来代替“智商、天赋和经验”!有很多人觉得,只要强制底层牛马画出足够多的 UML 图,开足够多的代码评审会,跟着详细流程走,他们最终就能搞懂业务并把代码写对。明确告诉你们:没有任何证据表明这种方法在过去奏效过,也没有理由相信以后会有用! 事实上,根据我一线的硬核经验,想要把 UML 这种抽象工具真正用对,所需要的专业知识和经验,比你直接去写代码还要高得多!

Tip

  • • 医生水平不行,你会给他制定一套“30步缝合标准流程”然后放他去给病人做心脏搭桥吗?
  • • 软件开发从来就不是传统工厂的流水线。它是一个高强度的知识密集型、创意型设计活动。

3. 工程师最终交付的是产品,而不是文档?

还有观点认为:“谁说工程的最终目标是产出文档了?人家正经工程师的目标是造出实体‘产品’好不好!火箭、大楼、汽车,那才叫工程产物!”

这帮人试图通过建立一种“传统工程师造实体”和“程序员写软件”的虚假平行关系,来逃避关于“什么是软件设计”的灵魂拷问。我明确告诉你这纯属扯淡!我承认,世界上确实有一些工程师不写设计文档(或者只在信封背面画画草图)就能把东西造出来。但你仔细看看,这种项目是一次性孤品(one-off products),而且通常是单打独斗的个人行为(比如隔壁王木匠打个凳子)。

一旦一个“工程项目”涉及的人数超过三两个人,或者它有一个正式的、规模化的工业制造阶段,那么设计文档就特么会无限膨胀,成为这个工程团队实际上交付的唯一「产品」!你真以为丰田或者摩托罗拉的工程师每天的工作是去车间里装发动机和焊电路板吗?更别提波音或者洛克希德·马丁(Lockheed)了!他们真正的、唯一的产物,就是高精度的设计图纸和制造工艺说明书!任何敢自称是工程师的人,都清清楚楚地知道自己领域的设计文档长啥样。

顺便说一句,这个关于“工程师的唯一产物是文档”的惊天观点,其实并不是我原创的。本人也是在 1979 年看《Datamation》杂志里的一篇文章时学到的。但我完全赞同它,并且它完美解释了软件圈的所有乱象!

Tip

既然传统工程师的产物是图纸(文档),那么软件工程师的产物——源码,就是软件的设计图纸。

4. 源码还是太抽象了,编译器吐出来的二进制码才配叫设计?

最后一个批判挺边缘、挺非主流的:源码还是太高级、太宏观了。他甚至想把源码降级定义为“需求规格”。他的观点是:编译器吐出来的那些 0 和 1 的二进制机器码,才配叫真正的“软件设计”。虽然表面看这只是一个「怎么定义设计」的问题,但我还是不同意这个观点。

业内公认的说法是:“需求规格”讲的是“做什么”(what),而设计文档管的是“怎么做”(how)。虽然编译器在把源代码翻译成机器码的时候,有一定自由度(比如编译器优化、寄存器分配),但是,这中间有半点“创造力”介入吗?我的界限就划在这儿:

只要一个文档写得足够详尽、足够没有歧义,可以被完全“机械化、无脑地”执行(不管是交给一台没有脑子的计算机去编译,还是交给流水线上的工人去组装)——那它就是设计文档。如果它还需要靠人的创造性去解读,那它就不算。

在软件开发中,那份设计文档就是源代码本身。

Summary

“工程的终点是创造力的结束,制造的起点是机械化执行的开始。”

Jack Reeves 在 1992、2005 年写下的那些预测和坚持,后续被整个软件行业逐一验证:

  • • 重度 UML 建模工具彻底走向衰落;
  • • “重构”与“自动化测试”成为行业第一宪法;
  • • 衍生出 DevOps 与“Infrastructure as Code”.

The design document is a source code listing”,这个三十多年前掀起的思想革命,已经彻底重塑了今天所有现代软件开发流程。