当前位置:首页>java>为什么 NASA 开发人员编写代码的方式截然不同

为什么 NASA 开发人员编写代码的方式截然不同

  • 2026-10-21 13:28:16
为什么 NASA 开发人员编写代码的方式截然不同
NASA 开发人员编写代码的方式截然不同

NASA Developers Write Code

硅谷痴迷于"快速交付"时,航空航天工程师正在编写 40 年来从未崩溃的代码。

当我们停止像对待火箭科学一样对待软件时,我们失去了什么。

作者:B V Sarath Chandra

在过去的十年里,我一直工作在"快速行动,打破常规"文化的核心地带。

我见过初创公司在周五部署上线。

我见过 30 秒写出的"热修复"。

我见过"完美是优秀的敌人"

这句口头禅被用来为内存泄漏、竞态条件和足以让银行家落泪的技术债务辩护。

然后,我遇到了一位来自喷气推进实验室的软件工程师。

我问他他们用什么框架。他笑了。

我问他他们多久部署一次。他不笑了。

"当你的代码在 1.4 亿英里之外时,"他说,"你是无法重启服务器的。"

当现代 Web 开发人员在争论 React 服务端组件与客户端组件时,航空航天工程师正在实践一门失传的艺术:软件即生存。

他们的代码不仅仅是运行;它能承受辐射、宇宙射线,以及 20 年无需一次重启的任务。

其秘诀?他们对待软件开发就像对待核反应堆,而不是一个待办事项应用。

改变一切的哲学

防御性设计:不信任任何人,包括你自己 在硅谷,我们编写假设成功的代码。

我们假设API 会响应。我们假设数据库在线。我们假设用户不会在年龄字段输入表情符号。

在航空航天领域,他们编写假设灾难的代码。

这被称为防御性设计。

当我查看火星探测车的代码时,我注意到一些奇怪的事情。每个函数都检查其输入。每个变量赋值都经过验证。

这让人感觉偏执。

"你为什么检查速度是否为数字?你刚刚在前一行把它设为一个数字!"我问道。

工程师回答:"因为在辐射强烈的环境中,内存中单个比特位翻转是一个真实且有记载的风险: 0 变成 1。

突然之间,探测车以 400 英里/小时的速度行驶,而不是 4 英里/小时。"

// 硅谷风格:"可能没问题。"function setRoverSpeed(targetSpeed) {  this.currentSpeed = targetSpeed;   // 如果 targetSpeed 是 "fast"(字符串)或 NaN,物理引擎会爆炸。}// NASA 风格:"信任物理,而非变量。"function setRoverSpeed(targetSpeed) {  // 1. 检查数据类型if (typeof targetSpeed !== 'number') {return ERROR_INVALID_INPUT;  }  // 2. 检查物理约束(探测车最高速度为 0.1 米/秒)if (targetSpeed < 0 || targetSpeed > MAX_DESIGN_LIMIT) {     logAnomaly("速度请求超出物理界限");return ERROR_UNSAFE_OPERATION;  }  // 3. 冗余状态验证  this.currentSpeed = targetSpeed;return SUCCESS;}

这种思维转变是深刻的。

Web 开发者:"如果失败,显示错误模态框。"

NASA 开发者:"如果失败,降落伞打不开,20 亿美元以终端速度砸向地面。"

"十大规则"的力量

NASA 的喷气推进实验室遵循一套严格的编码规则,称为"十大规则"。

这些规则会让你在黑客松上被嘲笑。但这就是旅行者号探测器在 47 年后仍在传回数据的原因。

规则 1:禁止动态内存分配

在初始化之后,禁止动态内存分配

在 JavaScript/Python 中,我们不断创建对象。

const user = new User()

垃圾回收器会清理烂摊子。

NASA 的飞行关键软件禁止在初始化后进行动态内存分配。

火箭一旦发射,你就不能要求更多 RAM。所有内存都是预先分配好的。

为什么?因为如果你从不请求内存,"内存不足"错误就不可能发生。

垃圾回收暂停是不可预测的。

教训:可预测性 > 便利性。

规则 2:禁止递归

递归很优雅,很优美。

但它也被禁止了。

为什么?因为递归可能导致无限循环或堆栈溢出。

固定循环结构如

for i in range(10)

是确定性的。

你能确切知道它运行需要多长时间。

"优雅的" Web 方式(风险:无限循环/堆栈溢出)
def find_root_node(node):if node.parent is None:return nodereturn find_root_node(node.parent) # 

如果存在循环怎么办?崩溃。

"无聊的"航空航天方式(风险:零)
def find_root_node(node):# 硬性限制:绝不信任数据结构是正确的    MAX_DEPTH = 1000 for _ in range(MAX_DEPTH):if node.parent is None:return node        node = node.parent# 如果达到限制,安全停止。不要崩溃。return ERROR_TREE_CYCLE_DETECTED

教训:如果你无法证明它何时停止,就不要开始。

规则 3:复杂性限制

函数应简短到能在一张纸上打印约 60 行。

这不仅仅是关于可读性。更是关于可验证性。如果一个函数超过 60 行,可能的执行路径数量会爆炸式增长。测试每个场景变得不可能。

"零缺陷"心态

玛格丽特·汉密尔顿的遗产

有一张著名照片,玛格丽特汉密尔顿站在一堆和她一样高的纸旁边。那是阿波罗导航计算机的代码。

完整历史重现请参见🔗

送阿波罗宇航员登月的代码充满喜剧式的英雄主义

火星课堂 | 为什么NASA的1960年代导航代码仍然优于谷歌地图?

那段代码让人类登上了月球。

"在阿波罗 11 号登月过程中,没有出现导致任务失败的软件错误。"

确实出现了硬件过载警报(1201 和 1202 警报),但软件完全按照设计执行:它优先处理关键任务(着陆),并放弃了低优先级任务(雷达更新)。

这不是运气。这是异步执行调度。

现代 Web 应用在你点击按钮过快时会崩溃。

阿波罗计算机在登月前 15 分钟处理了过载情况,只是说:"我很忙,我先忽略一下雷达。"

"无我"代码评审

在许多科技公司,代码评审是自尊心的较量。

"你为什么不用 map 函数?"或者"这不是 React 的方式。"

在关键系统工程中,他们实践无我编程。

代码不是"你的",它属于任务。

他们坐在房间里,将代码投影在墙上,逐行剖析。

如果有人在你的代码中发现错误,你不会感到防御,而是感到宽慰。

你会说:"谢谢,你刚刚拯救了这次任务。"

他们不为"风格"而评审。他们为以下目的评审:

•   如果输入为 null 会怎样?

•   如果循环运行 100 万次会怎样?

•   如果传感器刚好在这里断开连接会怎样?

结果说明一切

旅行者 1 号:终极遗留代码

旅行者 1 号于 1977 年发射。它的计算能力比你的汽车钥匙扣还弱。

它目前距离地球 150 亿英里,处于星际空间。

近年来,旅行者 1 号开始传回损坏的遥测数据。

工程师们借助 50 年前的文件,从地球上调试了一个损坏的内存芯片。

他们向一个在迪斯科流行时建造的计算机上传了一个补丁。

它成功了。

相比之下,现代的物联网智能冰箱会因为制造商关闭服务器而停止工作。

为什么这对你很重要?

"Web 开发"现实检验

你可能会想:"我构建的是电子商务网站,不是火箭。我不需要这个。"

但你需要。

因为"快速行动,打破常规"已经打破了一切。

•   我们有在发薪日宕机的银行应用。

•   我们有泄露患者数据的医疗门户。

•   我们有产生种族歧视幻觉的 AI 聊天机器人。

我们接受了"不稳定"作为速度的代价。

NASA 证明这是一个谎言。

如何将"火箭科学"应用于你的代码

你不需要编写汇编语言也能从中受益。

1.静态分析是你的安全网

NASA 使用工具来数学证明代码的正确性。

你可以使用严格模式下的 TypeScript。你可以使用 Linter。

将警告视为错误。

如果 Linter 报错,火箭就不能发射。

2.故障安全,而非故障即停 当 React 组件出错时,整个页面常常变白(白屏死机)。

那是"故障即停"。

故障安全意味着:如果"推荐商品"小组件失败,"加入购物车"按钮必须仍然有效。

隔离你的关键路径。

// "故障即停"(白屏死机)try {  loadRecommendations();} catch (error) {  // React 错误边界会捕获这个错误,但会卸载整个页面  throw new Error("组件失败"); }// "故障安全"(任务继续)try {  loadRecommendations();} catch (error) {  // 1. 记录崩溃*前*的状态  telemetry.log("推荐失败", systemState);  // 2. 隐藏该功能,不要杀死应用  this.showRecommendations = false;  // 3. 确保关键路径保持活动  // '结账'按钮是隔离的,并继续工作}

3.记录"原因",而不仅仅是"发生了什么" 不要只记录 Error: 500。

记录崩溃前系统的状态。

阿波罗工程师确切知道 1201 警报为何响起,因为系统设计为告诉他们为什么过载,而不仅仅是过载了。

4.文档的"巴士因子"

如果你明天被巴士撞了,你的团队能部署你的代码吗?

NASA 的文档是传奇性的。他们为尚未出生的人编写手册。

编写你的 README,就像阅读它的人需要在 20 年后的凌晨 3 点调试你的代码,而你正处于低温睡眠舱中一样。

文化转变 从"编码员"到"工程师"的转变是思想上的。

•   编码员问:"它能用吗?"

•   工程师问:"当它停止工作时,会发生什么?"

我们生活在一个由软件运行的世界。我们的汽车、我们的心脏起搏器、我们的电网、我们的财务。

也许现在是时候停止将我们的代码视为一次性玩具,而开始将其视为任务关键的仪器了。

你不必建造火箭也能拥有火箭科学家的思维。

你只需要下定决心,失败不是一个可选项。

你是否曾经在一个不允许失败的系统上工作过?它是如何改变你的编码风格的?在评论中告诉我。

“快速迭代、打破常规”的硅谷文化之外,还存在一种如航天工程般严谨、以“生存”为最高准则的软件开发哲学。

维度 硅谷模式(“快速行动,打破常规”) NASA/JPL 模式(“软件即生存”)

核心目标 快速交付、抢占市场、容忍失败 绝对可靠,任务成功高于一切

错误成本 宕机、用户体验不佳、收入损失(可修复) 任务失败、数十亿美元损失、人命关天(不可逆)

设计理念 乐观设计:假设一切顺利 防御性设计:假设一切都会出错

对失败的态度 失败是成功的垫脚石,可快速回滚 失败不是选项

NASA 开发方法的三大支柱

https://ntrs.nasa.gov/citations/19900017394

NASA 实现极高可靠性的方法论建立在三大基石之上:

  1. 严格的编码规则:“10 条军规”

这些规则看似“反敏捷”,但旨在消除一切不确定性:

•   禁止动态内存分配:系统初始化后不再申请新内存,杜绝了“内存不足”错误和垃圾回收带来的不可预测延迟。

•   禁止递归:使用循环替代递归,并为循环设置明确的上限,防止无限循环或栈溢出。

•   限制函数复杂度:每个函数必须能在一张纸内打印完(约60行),以确保其逻辑可被完全理解和测试。

  1. “零缺陷”心态与文化

•   Egoless 代码评审:代码不属于个人,而属于任务。评审目的是“找出能摧毁任务的漏洞”,而非争论代码风格。发现漏洞是值得感谢的事情。

•   历史典范:阿波罗11号登月时计算机过载,软件成功抛弃非核心任务,优先保障了着陆。这并非运气,而是精心设计的“异步执行调度”的结果。

  1. 极致的可维护性与文档

•   传奇的文档:旅行者1号探测器1977年发射在50年后出现故障,工程师依然能凭借详尽的文档,在数亿公里外完成修复。这要求文档必须能让未来的人(甚至是尚未出生的人)理解。

•   “巴士因子”:确保任何成员离开都不会对项目造成致命影响。

如何将“火箭科学”思维应用于日常开发?

将静态分析作为安全网:严格使用 TypeScript 的严格模式和各种 Linter 工具。

将警告视为错误,在代码合并前消除所有隐患。

设计“故障安全”而非“故障崩溃”的机制:

当非核心功能如“推荐商品”失败时,不应导致核心流程如“下单付款”崩溃。需要进行隔离设计。

记录“为何出错”而不仅是“出了何错”:日志不仅要记录错误信息,还要记录错误发生前的系统状态,这对于复盘和调试至关重要。

编写面向未来的文档:假设你的同事需要在凌晨三点紧急修复你的代码,而你已经联系不上。文档应清晰到足以应对这种场景。

根本性的思维转变

从“程序员”到“工程师”的转变在于一个问题:

  • 程序员问:“它能运行吗?”

  • 工程师问:“当它停止运行时,会发生什么?”

工程精神的回归。

由软件驱动汽车、电网、金融的时代,重新思考,继续将代码视为可快速迭代的消耗品,还是开始将其作为关乎重大的关键系统来对待。

您不一定建造火箭,但可以拥有火箭科学家的思维即下定决心,让失败不再是一个可接受的选项。

再次重温那张著名照片里玛格丽特汉密尔顿站在一堆和她一样高的纸旁边。那是阿波罗导航计算机的代码。

完整历史重现请参见🔗

送阿波罗宇航员登月的代码充满喜剧式的英雄主义

火星课堂 | 为什么NASA的1960年代导航代码仍然优于谷歌地图?

仅仅沧海一粟中的 ... NASA技术文档摘要:


第一篇文档

标题:用于嵌入式软件系统的基于代码模式的演绎式粘合代码合成

摘要:

自动代码合成是一种能够根据规约生成程序的构建性过程,可显著降低软件开发成本与时间。

采用形式化代码合成方法进行软件生成能进一步提升系统的可靠性。尽管代码合成具有诸多潜在优势,但现有合成技术仍有局限。

同时,嵌入式系统开发中广泛使用组件。将代码合成应用于基于组件的软件开发过程,能极大增强代码合成能力,并减少组件组装的工作量。

本文探讨了在基于组件的软件开发中应用演绎式代码合成技术的问题与方法。对于基于组件的演绎式合成,规则库是推断适当组件组装的关键。

我们使用代码模式来指导规则的开发。

代码模式被提出用于捕捉组件的典型使用方式。文中还识别了几种通用的组合操作,以促进系统化的组合。

我们介绍了规则开发以及从现有代码模式自动生成新模式的技术。最后,展示了一个在构建实时控制系统中使用此方法的案例研究。

文档ID:20070023650

获取来源:喷气推进实验室

文档类型:预印本(送交期刊的草稿)

外部来源:hdl:2014/40116

作者:刘健 (德克萨斯大学达拉斯分校,德克萨斯州,美国)、傅继成 (德克萨斯大学达拉斯分校,德克萨斯州,美国)、张延生 (德克萨斯大学达拉斯分校,德克萨斯州,美国)、Bastani, Farokh (德克萨斯大学达拉斯分校,德克萨斯州,美国)、Yen, I-Ling (德克萨斯大学达拉斯分校,德克萨斯州,美国) 等

获取日期:2013年8月23日

发布日期:2006年6月14日

主题分类:计算机编程与软件

会议信息:会议:ACM Sig Plan/Sig Bed 嵌入式系统语言、编译器与工具会议

地点:渥太华

国家:加拿大

开始日期:2006年6月14日

分发限制:公开

版权:其他

关键词:自动代码合成,演绎式代码合成,代码模式,实时系统


第二篇文档

标题:一种自动代码合成器

摘要:

本文描述了一种我们称之为ACG的自动代码合成器的设计与架构。ACG的输入是一个必须为生成代码所满足的事实列表,以及领域特定知识设计规则和模式。

文档ID:20060036386

获取来源:喷气推进实验室

文档类型:预印本(送交期刊的草稿)

外部来源:hdl:2014/26080

作者:Reinholtz, W. K.

获取日期:2013年8月23日

发布日期:1996年1月1日

分发限制:公开

版权:其他

关键词:编程,编程代码,编程生产力,代码架构,程序设计,计算机程序,应用程序员


核心要点总结:

这两篇NASA JPL的文档探讨了自动代码合成技术,特别是应用于高可靠性嵌入式系统领域。

  1. 第一篇(2006年)提出了一个基于代码模式的演绎式方法,用于合成连接组件的"粘合代码",旨在提升基于组件开发的可靠性和效率,并以实时控制系统为例进行了验证。

  2. 第二篇(1996年)更早地介绍了一个名为ACG的自动代码合成器原型,其核心思想是依据事实列表和领域知识(规则与模式) 来自动生成代码。

NASA在形式化、自动化软件生成方面的早期探索,其目标是通过提高代码生成的可靠性和自动化程度,来应对嵌入式系统,尤其是航天任务中软件的高安全、高可靠开发挑战。

最新文章

随机文章