---
title: "黑箱转向（一）：软件工程从来不是完全的白箱"
url: https://lingming.blog/posts/2026/the-white-box-assumption/
date: 2026-03-02
lastmod: 2026-08-29
tags: ["软件工程","模块化","信息隐藏","可理解性","软件史"]
categories: ["软件工程","计算机科学"]
description: "软件工程曾经是完全透明的白箱吗？本文回到结构化编程和信息隐藏的历史，区分源码可见、可理解、可预测与可控制。"
---

# 黑箱转向（一）：软件工程从来不是完全的白箱


如果把一个现代浏览器的全部源码打印出来，没有任何一页被隐藏，它就是白箱吗？

从访问权限看，是。从个体理解看，不是。从团队能否定位一次故障、判断一次改动或恢复一个版本看，答案又取决于文档、测试、观测和组织分工。把这些问题都压进“透明”一词，容易虚构出一个从完整白箱突然坠入 AI 黑箱的历史。

[总序]({{< relref "/posts/2026/the-manifesto-for-software-engineering-after-human-comprehension.md" >}})提出，AI 扩大的是生成—验证落差。本篇向后退一步：传统软件工程究竟追求过哪一种可理解性？

## 结构化编程限制了理解负担

Dijkstra 在 1968 年反对不受约束的跳转，重点并不是让程序像散文一样“好看”，而是让程序运行过程与人的概念结构更容易对应。顺序、选择和循环等受控结构，缩小了工程师必须同时追踪的程序状态。

这揭示了软件工程一个持续至今的目标：不是把全部复杂性消灭，而是让局部推理有边界。一个函数内部的控制流更清楚，工程师就能在有限范围内判断前置条件、后置结果和可能副作用。

但局部可推理并不等于全局可穷尽。即使每个函数都写得清晰，并发、配置、数据和组件组合仍可能产生难以预料的系统行为。结构化编程提高了可理解性，没有建立“一个心智可以完整覆盖整个系统”的保证。

## 信息隐藏主动制造了可用的黑箱

Parnas 在 1972 年讨论模块分解时，把信息隐藏作为重要标准：模块应隐藏可能变化的设计决定，通过稳定接口降低其他模块对内部知识的依赖。

这意味着成熟的软件工程从一开始就在主动制造局部黑箱。调用数据库接口的业务工程师不必理解查询优化器的全部实现，应用开发者也不必掌握操作系统调度器的每条路径。只要契约足够稳定、失败模式得到说明，隐藏细节反而提高了整个组织的可操作性。

所以，抽象不是对透明性的背叛，而是对注意力的分配。它把“所有人都要理解一切”改成：

- 模块维护者理解内部关键机制；
- 使用者理解接口、保证和失败方式；
- 组织保存跨模块决策与运行证据；
- 高风险部分接受更深的独立检查。

这种分层理解比“某位架构师完整掌握系统”更接近大型软件的真实工作方式。

## 五个经常被混用的问题

讨论白箱与黑箱时，至少应区分五个维度。

| 维度 | 核心问题 | 反例 |
| --- | --- | --- |
| 可见性 | 能否取得源码、配置和模型信息？ | 开源代码全部可见，但规模巨大 |
| 可理解性 | 目标读者能否建立足够心智模型？ | 代码可读，却缺少领域背景 |
| 可追溯性 | 变更、依赖与决策能否回溯？ | Git 有提交，但需求理由未记录 |
| 可预测性 | 给定条件下能否预期行为？ | 并发竞态使结果依赖时序 |
| 可控制性 | 能否限制、发现并纠正越界行为？ | 系统易懂，却没有权限隔离和恢复机制 |

源码可见并不保证理解，理解也不保证预测。反过来，一个内部实现不公开的组件，也可能通过严格契约、充分测试和可替换设计而保持可控制。白箱与黑箱不是单一开关，而是一组不同性质。

## 代码审查和版本控制真正提供什么

代码审查并不要求审查者重新构造整个系统。它更现实的作用是检查一组有限变更：意图是否合理，局部实现是否符合约定，风险是否被识别，测试是否与主张匹配。审查质量取决于变更大小、上下文、专业知识和工具支持，而不是代码出自人还是模型。

版本控制同样不保证变更“可以被解释”。它可靠地记录文本差异、作者身份和时间，却未必保存为什么选择这种设计、放弃了哪些方案，或者上线后出现了什么新事实。Diff 是证据的一部分，不是完整的因果叙事。

AI 生成代码的特殊问题，也因此不只是“作者不再亲手敲字”。如果生成内容保持小批量，有明确需求，经过有效审查并附带测试，传统机制仍然工作。如果一次生成跨越大量模块，意图只存在于短暂对话中，审查者又主要确认“能运行”，那么可见的源码仍会伴随严重的理解缺口。

## 大型软件早已依靠不完全理解运行

Brooks 在《没有银弹》中把复杂性、顺应性、可变性和不可见性视为软件的固有困难。这个判断早于生成式 AI 数十年。今天的依赖网络、云服务和持续部署放大了这些困难，但没有创造它们。

因此，传统软件工程更准确的历史不是：

> 人类曾理解完整系统，后来 AI 夺走了透明性。

而是：

> 人类长期通过抽象、专业分工和多种证据，在无法完整理解的系统中获得局部可信度；AI 让实现增长得更快，也可能打乱原本维持这种可信度的节奏。

这个改写很重要。若误以为过去依靠“逐行理解一切”，我们就会把既有工程实践低估为过时方法；若误以为未来只需看测试结果，又会丢掉解释、审查和调试仍然提供的独特价值。

## “白箱假设”应当怎样保留

白箱假设仍可以作为一个有限的规范理想：

1. 关键行为不应故意逃避检查。
2. 重要变更应留下可追溯的意图与证据。
3. 负责维护的人应能解释与其职责相称的系统范围。
4. 在安全、财务、身份和权限等高风险区域，应提高透明性和独立验证要求。

它不要求每个人理解每一行，也不要求所有组件公开全部内部机制。真正需要防止的不是抽象，而是“没有人知道、没有证据支持、也没有恢复路径”的三重空白。

AI 时代不是从白箱走向黑箱，而是迫使我们承认：软件一直由不同透明度的区域组成。问题不在于是否允许黑箱，而在于谁可以把什么当作黑箱，以及他必须用什么证据来换取这种信任。

## 延伸阅读与参考资料

- [E. W. Dijkstra：Go To Statement Considered Harmful](https://ir.cwi.nl/pub/28228)
- [D. L. Parnas：On the Criteria To Be Used in Decomposing Systems into Modules](https://doi.org/10.1145/361598.361623)
- [Frederick P. Brooks Jr.：No Silver Bullet—Essence and Accidents of Software Engineering](https://www.cs.unc.edu/techreports/86-020.pdf)

*上一篇：[《黑箱转向（总序）：当生成速度超过验证速度》]({{< relref "/posts/2026/the-manifesto-for-software-engineering-after-human-comprehension.md" >}})；下一篇：[《黑箱转向（二）：生成—验证落差，真正的拐点》]({{< relref "/posts/2026/the-generative-inflection-point.md" >}})。*

