第一章 从沙砾到地基
1.1 代码为何必须被工程化——从手工艺到工业化
巴比伦——世界上第一部建筑规范
公元前1750年。巴比伦。汉谟拉比王颁布了一部刻在黑色玄武岩石柱上的法典。282条法律,覆盖了当时文明的每一个方面。第229条是这样写的:
“如果一个建筑者为别人建造了一座房屋,而这座房屋倒塌了,导致屋主死亡,那么这位建筑者应当被处死。”
第230条接着写:如果屋主的儿子死了,那么建筑者的儿子也应被处死。
四千年前,人类就已经知道:建造关乎人命。你不能靠感觉、靠经验、靠“我做了三十年、从来没倒过“来保证安全。 你需要规则,需要可以被传承、被验证、被问责的规则。
汉谟拉比法典里的这条建筑规范,是人类历史上最早的“工程认证“:在建筑倒塌发生之前,就用规则的威慑力倒逼建造者认真考虑结构的每一个细节,而不是等倒塌之后再去追责。后来的维特鲁威、宋代的《营造法式》、19世纪的伦敦建筑法案,所有建筑规范的演进都被同一个逻辑驱动:“如果倒了会死人,就不能只靠工匠的直觉。”
你写的方向盘控制代码,在第229条的意义上,和巴比伦的一块石基同构。区别只有两处。一块石基能承多少重,可以用肉眼看见、用公式算准。一个int16_t溢出在左转打死方向盘时的符号翻转,你看不见它。它不裂、不歪。它只是一次计算,在540°变成了-540°,然后方向盘在自己对抗驾驶员。
汉谟拉比没有预见到Cortex-R5,但他的问题留下来了:你怎么证明你造的这件东西不会伤人?
手工艺的逻辑极限
建造的历史,有一条清晰的分界线:从手工艺到工程。
中世纪的大教堂是手工艺的巅峰。一个石匠大师傅可以从学徒干到入土,他脑子里装着一整个时代的经验:哪个采石场的石头适合做拱顶、什么季节砌墙不容易裂、多高的塔楼在风力下是安全的。他的大脑是唯一的“建筑规范“。他死了,这些东西可能就带走了,下一个人再用三十年重新积累。
这就是手工艺的逻辑极限:知识在人的脑子里,不在系统里。人死则道消。 你可以造出一座惊艳的哥特教堂,但不能保证下一座也是同样安全的。因为图纸可以复刻,经验不能。
软件工程,直到今天,在很多领域仍然是手工艺。一个资深的程序员和一个刚毕业的程序员,他们写出的同一个功能的代码,在执行效率上可能差5倍,在bug密度上可能差10倍。这个差距和数据结构的选型没关系,和经验有关系。经验在人的脑子里,不在代码库里。
但汽车嵌入式软件不能接受这个差距。不能接受“这个老工程师调走了,下一个接手的要交两轮事故当学费“。因为学费是人命。
这就是“工程化“的本义:把藏在单个人脑子的知识,变成可以被多个人执行、可以被工具验证、可以被后来者复现的系统。 要确保的,是人的判断不随着那个人一起离开。
两座桥的故事——工程如何从事故中长出来
你站在魁北克大桥前。1907年8月29日下午5点32分,这座正在施工的钢悬臂桥在15秒内从中间塌入圣劳伦斯河。75名工人死亡。调查结论只有一个词:重量误算。项目总工程师西奥多·库珀从来没有亲自到过现场。他反复催促加长主跨长度,却忽略了加长之后钢桁架的自重增加。他的结构计算里,有一个“估计错了大概2000吨“的误差。
大约十年后,同一个地点,第二代魁北克大桥建成,至今还在用。从倒塌到建成,加拿大工程学界做了一件事:把“不许在远程做决策“写进了职业伦理规范。 工程师必须在现场、用自己的眼睛看过、确认过。
第二个故事。1940年11月7日。塔科马海峡大桥。风速只有64km/h,不到设计抗风标准的一半。但这座桥在风中扭曲了45分钟,像一个巨大的绸带,然后塌入水中。没有人在桥上,一只狗在弃车里死了。调查结论让所有结构工程师沉默了:大桥的抗风设计使用了静力计算,它假设风是“推“桥的,但没有考虑风在桥面和主缆之间产生了一连串交替涡旋,这些涡旋恰好和桥面主缆的固有频率共振。 结构是够强的,但模型不够精确。
这两座倒塌的桥,重塑了全世界的土木工程方法论。从此以后,没有一座桥梁不经过风洞测试:实体模型,实际气流,真实地承受物理世界的抖动。在纸上完美的计算,必须被物理世界打脸一次,才算经过验证。
你写的单元测试、集成测试、HIL测试,本质上就是你的ECU代码的“风洞测试“。你在纸面上写好的控制逻辑,必须被注入故障、被边界值冲刷、被最坏时间组合推挤,才能证明你的模型也是“够精确的“。
一座桥的教训——以及你为什么不需要重蹈覆辙
塔科马大桥倒塌后,全世界的桥梁工程师不仅仅是改了一下风洞测试流程就结束了。他们做了一件事:把“共振“写进了每一个结构工程本科生的必修课。 从此以后,任何一个学过结构工程的人都知道“你的模型必须考虑动态响应“,是因为一次死亡换来的教训被固化成了可传承的知识。
这就是工程化的本质区别:手工艺的知识是靠“一个人带一个徒弟三十年“传承的。工程的知识是靠“一次事故→一次分析→一条规范→写入教材→所有人以后都不需要自己再体验一次那个事故“来传承的。
你现在学MISRA C规则17.2“禁止递归函数“,你不需要自己去实车测试一次“递归爆栈导致方向盘在高速上锁死“。因为这个事故已经发生过了,在某个测试场、某个原型车的一次惊险的挽救中,被一位你没有见过面的工程师记录了下来,变成了一条禁止性规则。你遵守它的代价只是换一种写法。你不遵守它的代价,可能是某个你没见过面的驾驶员,在一个你没去过的城市的雨夜,经历一次你永远不知道的事故。
这就是规范背后的人。 每一条MISRA规则、每一个ASPICE过程要求、每一个ISO 26262的ASIL判定都不是一个委员会的会议室里拍脑袋的结果。它们是活生生的人、真实的事故、和血的代价被翻译成了你能在IDE里对照执行的标准。
你写的代码会陪一个人走过他最危险的时刻
说一个你在汽车行业的教科书里不会看到的真相:你写的代码,永远不会出现在你的生命里。它出现的地方,是别人的生命。
你写的那个方向盘转角计算函数,它会在一个下着暴雨的夜晚、在一个你从来没去过的省份的盘山公路上、在一个40岁父亲以60km/h带着后座上熟睡的女儿回家的时刻,被调用。如果这个函数在那一刻算错了一个值。可能不到200毫秒之后,护栏已经在撞上的一瞬间,一个孩子再也不会醒过来。
你不用觉得这太沉重。你只需要知道,这就是你为什么不能只靠“我测过了、跑了三天没有复位“来确认安全。这就是为什么你需要ASPICE、MISRA C、代码审查、静态分析、单元测试、集成测试、MC/DC覆盖率。这些不是官僚垃圾,它们是200毫秒的护栏前最后一道防线。
如果你做得对,什么都不会发生。 你写的代码安静地在那辆车上跑了十几年、几十万公里,方向盘从未偏过,制动从未失效,看门狗从未被触发。没有人知道你做了什么。你的父母不知道、你的配偶不知道、你的孩子不知道,甚至那辆车的车主也不知道。“一切正常“之外,没有任何称赞。功能安全的最高境界是:没有人因为你是安全的而感谢你。但每一个没有发生的死亡,都是你写过的测试、做过的审查、追过的追溯链、在深夜对着静态分析报告逐条排查误报的证据。
工程化的真正含义——三件事
所以,“把你的代码工程化“到底意味着什么?
第一件事:规则化的知识。 汉谟拉比法典→MISRA C规则。禁递归、禁动态内存分配、禁goto不是审美,是“不允许你依赖一个不可验证的经验“。每一条MISRA规则的背后,都有一个真实发生过的事故,就像魁北克大桥之后“不许远程做决策“一样。
第二件事:可被第二个人验证。 代码审查、静态分析、单元测试不是找bug的工具,它们是第二双眼睛。哥特石匠的经验在他脑子里,教堂建好没人能打开他的脑子看一眼“它真的够强“。你的代码写完,静态分析工具打开你的函数,它看到了所有的路径,它不累、不偏心、不跳行。它用算法验证了“你写的和你认为你写的“是否一致。
第三件事:持久化的知识。 建筑图纸可以保存百年。代码+文档+追溯矩阵+架构描述,同样可以。保存的意义在于,以后出问题了有人能追到源头。负责施工的人不是100年前的设计师,但他拿着当年的图纸,知道承重墙在哪。十五年后你代码的维护者不是你自己。他能不能从你的需求追溯矩阵找到“哦,原来这段代码是因为HARA的S3评分才被设成最高优先级运行的“?
为什么这本书不是通用的软件工程书
如果你来自互联网的世界,欢迎。你那边做的是让一亿人同时刷视频不卡。你处理的是分布式一致性问题,是毫秒级的延迟抖动,是灰度发布和热回滚。你那边也很难,难的维度不一样。
这本书面对的维度,可能是只有一个人。这个人以120km/h在高速上,要求是“不失控“。灰度发布在这里也不存在。Flash烧录之后的几千辆车已经在路上,回滚需要召回。
你在这本书里会遇到一些让你本能地想“这也太重了吧“的实践,比如写三行代码要花半天写追溯矩阵。你在你那边养成的直觉(“快速迭代、用户反馈驱动、先上线再优化”),在这里会被反复挑战。不是你的直觉错了,是这里的博弈规则变了。后果从“用户投诉“变成了“可能有人会死“。
两边都难。你带来的那些东西(自动化思维、对工具的敏感度、对重复劳动的零容忍),在这边一样有用。只是这边的生存法则是另外几条。
人工智能时代的软件工程——不变的核心
三年前,ChatGPT写了一段C代码,比你写得快。去年,Cursor帮你重构了一个函数,比你重构得好。今年,一个AI模型通过了LeetCode所有题目。软件工程这个职业是不是要完了?
你想过没有:AI能写代码,但AI不知道“安全“是什么意思。
“安全“不是一个技术指标,它是人类社会的价值判断。ASIL D是一群工程师和功能安全专家围坐在会议室里,看着HARA表上S3那一栏(Life-threatening, survival uncertain),投票决定的。AI可以帮你生成一个单元测试,但它不能替你决定“这个转向功能需要ASIL D还是ASIL B”。因为这个决定是道德判断,而不是技术判断。它回答的是“我们愿意为多一分安全付多少钱“的问题。AI没有道德,AI只有概率。
而且:AI写代码越快,破坏也越快。 一个真实的案例:某开发者用AI生成了一个大版本的API更新。代码看起来没问题,通过了编译,但它改了函数签名而没有更新所有调用者。旧客户端调用时崩了。一行崩溃,几百万用户受影响。在没有CI门禁、没有回归测试、没有代码审查的情况下,AI=风险加速度×10。有了CI门禁、有了回归测试、有了代码审查→AI=效率加速度×10。
软件工程在AI时代不仅不过时,而且更必要了。 因为工具变强之后,区分“写代码“和“写对代码“的边界变得更尖锐了。AI接手了“写代码“,你接手了“写对代码“(验证、追溯、审查、测试)。这是升级,不是衰落。
三线编织——显、明、隐
你在接下来的章节中会发现,这本书始终在三条线上编织:
显线:建筑学隐喻。 模块是砖块,接口是门窗,架构是结构设计,测试是监理验收。这是你看得见的。
明线:汽车嵌入式约束。 安全关键(可致死)、资源极度受限(64KB SRAM)、生命周期15年+、认证审计(ASPICE+ISO 26262)。这是你想得到的。
隐线:以人为本。 所有方法论、规范、工具、流程,最终都是为了保护人:驾驶员安全到家,十五年后的维护工程师不骂你,调查者出事故时能沿着链追到源头。这是你读到一半时会感觉到的。
这本书就是这三条线的编织体。没有第四条。你看到模块化的时候,同时看到了建筑学的砖块(显)、看到了ASIL D的单点故障约束(明)、看到了“下一个接手的工程师能不能看懂这个接口“(隐)。三条线重叠在同一个段落里,而且它们不打架,它们相互解释。这是这本书的底层写作结构。你知道它存在就够了。
核心洞察:工程化的本质是承认你是一个人。你会遗忘,你会疲劳,你会漏看一个边界条件,你会在凌晨三点写完代码但一周后记不清为什么要那样写。所有工程化实践(版本控制、代码审查、静态分析、测试、追溯链)都是围绕“人不可靠“这一基本事实建立起来的防御层。你不是在给代码加负担,你是在给自己建立第二道保险:一道不会累、不会忘、不会赶时间的防线。
本篇小结
- 汉谟拉比法典第229条是人类最早的工程认证:建筑危及人命,不能只靠工匠直觉。汽车软件同构:你需要证明你造的不会杀人。
- 工程化的本义:把藏在个人脑子里的经验变成可传承、可验证、可复现的系统。手工艺→工业化。因为工匠会死,经验必须被固化下来。
- 魁北克大桥和塔科马大桥两次倒塌重塑了土木工程方法论:错误模型、远程决策、共振忽略。单元测试和HIL测试就是汽车软件的“风洞测试“。
- 工程化的三件事:规则化知识(MISRA C)、可被第二人验证(static analysis/code review)、持久化知识(追溯链+文档)。
- 本书三条线:建筑学隐喻(显)、汽车嵌入式约束(明)、以人为本(隐)。一切为了人。
【下集预告】:汉谟拉比是建造规范的起点。软件工程的“汉谟拉比时刻“来得晚得多:1968年。德国小镇加米施,50名顶尖程序员坐在一起,给一个普遍存在的现象取了名字:软件危机。IBM的弗雷德·布鲁克斯刚花了五年管理System/360的操作系统开发,他用一本书总结了一切:《人月神话》。而艾伦·图灵在30年前就用停机问题证明了一个让所有人无力反驳的结论:完美不存在。进入1968。