ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

编程作为理论构建:Naur的误读与团队知识传承

编程作为理论构建:Naur的误读与团队知识传承 一个项目在核心开发者离职之后突然像被抽走了灵魂。代码在注释在文档也在但新团队每次改需求都像拆炸弹不敢动、看不懂、改完害怕爆炸。这个现象很多老工程师都能讲出一堆原因——模块耦合、文档过时、代码风格混乱。但 Peter Naur 给出的解释要底层得多程序不是一段文本而是一个“理论”。理解程序不是读懂文本而是在头脑中重建当年开发者构建的那套理论。理论一旦丢失代码就只是一堆符号。这是 Naur 在上世纪八十年代那篇著名论文《Programming as Theory Building》里的核心主张。这篇论文被无数软件工程讨论反复引用也是“软件工程”这个主流隐喻的强有力批评。更有意思的是Naur 在构建这个思想时借用了哲学家 Gilbert Ryle 的《The Concept of Mind》心的概念。但恰恰是在这个借用过程中我认为 Naur 出现了一个值得认真讨论的误读他把 Ryle 用来反对私人认知二元论的概念重新变成了一种藏在个人脑中的、不可外传的私人理论。这个误读的后果并不只是理论层面的争论。它直接影响了我们对文档、代码评审、团队协作、知识传承的态度。如果相信理论只存在于个人头脑中那文档就是摆设代码只是附带产物沟通和评审也只能靠“神交”。这会塑造一种非常不健康的技术文化。所以今天想从一个工程实践者的角度把“Naur 从哪里 got wrong from The Concept of Mind”拆开聊一聊。1. 程序文本不等于程序Naur 的核心判断1.1 为什么代码库会“失智”开头提到的那个离职场景是观察“理论构建”概念最好的入口。代码库仍然完整甚至单元测试都能通过但新团队修改一个功能时不知道它为什么依赖另一个模块不知道某个魔法数字为什么是 7 不是 8不知道一个看似多余的参数为什么不能删。这些问题根源不是说文档写得不全而是“知识”没有被文本完全捕获。Naur 把这种知识称为“理论”。在他看来程序员通过开发活动逐渐形成关于这个程序如何运行、如何使用、为什么这样设计的理解。这个理解不是从文档里读来的而是在构建程序的过程中逐步形成的。程序文本是理论的外壳理论本身则是程序员头脑中的一套知识结构。所以当核心开发者离开时带走的不是代码而是那套理论。文本可以被工具、版本库、文档完整保存理论不会。这正是很多重老项目让新人感到无力的原因——他们面对的是一堆行为的化石而不是活着的行为。我接过一个维护了七八年的老系统第一周读代码时所有逻辑都能看懂commit message 也很规范。但真正开始改第一个需求时我发现我无法回答“为什么这个模块只允许单线程处理”。代码里没有任何锁看起来也支持并发但一个资深同事拦住我说千万别并发调用否则某个底层接口会随机超时。这个“知道”根本不在代码里它存在于一个已经离职的人对底层设备行为的一次意外观察中。这就是理论断裂。1.2 “理论构建”不是“程序编写”Naur 这个论断最锋利的指向是“软件工程”这个主流隐喻。软件工程喜欢把开发拆成需求分析、设计、编码、测试、维护强调流程、产出物和可度量性。但 Naur 认为真正的编程活动不是生产文本而是构建理论。代码只是理论的副产品测试只是理论在某几个例子上的表现。这个观点很反直觉。我们能够看见的产出物是代码但 Naur 坚持“程序”在本质上是一个心智活动的结果。这就像一栋建筑图纸和砖墙固然重要但更关键的是设计者脑中关于这座建筑如何受力、如何被使用的整体概念。建筑施工可以外包给工人但概念一旦丢失建筑就只能被模仿不能被演进。这个判断帮我解释了许多“代码能跑但改不动”的项目。团队每天都在产出代码但很少刻意维护那套关于“为什么”的理论。于是代码库越来越大问题也越来越多。更麻烦的是现代工具链倾向于把“代码生产”数字化提交数、行数、覆盖率、Issue 关闭率。这些东西很容易度量但它们衡量的是文本生产的速度而不是理论构建的深度。Naur 的论点之所以半个世纪后还有生命力正是因为它戳中了这种错位我们用了大量工具去管理文本却没有多少工具去管理理解本身。2. Naur 从《心的概念》里拿来的东西2.1 Ryle 反对“机器中的幽灵”《The Concept of Mind》出版于 1949 年作者 Gilbert Ryle 是牛津哲学的日常语言学派代表。这本书最广为人知的贡献是批判了笛卡尔式的身心二元论。Ryle 把这种二元论叫做“机器中的幽灵”认为人的身体是一台机器心是藏在机器里的幽灵负责思考、决策和感觉。Ryle 认为这是一个范畴错误。他把“心”看作一种能力或倾向而不是一个独立的实体。“知道如何骑车”不是大脑中有什么神秘理论而是你能够在各种路况下保持平衡的行动能力。“知道某个事实”才是拥有某种知识但 Ryle 特别强调“知道如何”不能还原为“知道那个事实”。一个人可以背出游泳规则却不会游泳。这个区分对 Naur 非常有吸引力。编程看起来就是一种“知道如何”你很难说清楚自己是怎么写出来的但你确实会写。代码中那些“说不清但就是知道”的判断仿佛正是 Ryle 所说的能力倾向。2.2 Naur 把“理论”和“操作知识”绑在一起在《Programming as Theory Building》里Naur 直接借用了 Ryle 关于“knowing that”和“knowing how”的区分。他说程序员对程序的理解不是“知道一组事实”而是“掌握一个理论”。这个理论能够指导程序员在任何未来情境下对程序做出判断这个改动能加吗这段代码安全吗某种异常会出现吗这个做法是很巧妙的。Naur 把程序开发的本质从“写文本”挪到了“构建个人能力”正好呼应了 Ryle 对能力型知识的强调。也因为这个原因很多人把 Naur 的论文当作“技艺派”的代表编程不是流水线而是工匠的心智活动。但问题恰恰就出在这个“心智活动”上。Naur 一边说理论是程序员脑中的私有结构一边又说它能指导所有未来的行动。听起来很完整可这种“私有结构”恰恰是 Ryle 最警惕的东西。Ryle 警告过我们不要把“知道如何”解释成某个看不见的内部事件。它应该体现在外部行动中而不是藏在一个神秘的“心智空间”里。3. 误读的裂缝Naur 把理论重新变成了“私人幽灵”3.1 Ryle 强调行为Naur 强调隐秘理解Ryle 反对“机器中的幽灵”并不是说大脑中不存在知识而是说我们不能把一个能力解释为一个藏在内部的“看不见的东西”。理解一个动作要看这个人在各种情况下如何行动。会游泳就是你能在水里做出协调动作理解程序应该是你能够对程序做出恰当修改和预测。Naur 在借用这个概念时却把“理论”描述成了一种个人头脑中的、无法完整言说的秘密结构。他明确说程序文本无法表达理论程序员必须“在脑中持有”理论因此理解程序只能靠参与构建或重新构建理论。听起来很有道理但这个表述有意无意地把理论推得远远的成了一种我们只能接近、却永远无法直接共享的“幽灵”。这里有一个关键差异。对 Ryle 来说所谓“拥有一个理论”等于你能够表现出符合那套理论的行为。对 Naur 来说理论更像是一个先于行为而存在的内部模型。模型当然存在于脑中但工程上我们无法直接观察它。于是Naur 的表述很容易滑向一个结论真正重要的东西不可说可说的东西不重要。3.2 这会带来哪些工程后果如果严格按照“理论不可言说”的推论代码评审、文档、架构图、注释都只是二手线索真正的知识只能在个人脑中。这会造成几个非常实际的问题。第一文档失去意义。既然理论无法完整表达那写文档就是白费力气。现实中许多团队确实因此不写设计文档或者只写“系统包含哪些模块”这种没人看的说明。第二评审流于形式。评审只能看代码风格和边界条件看不到“理论”对不对因为理论不在代码里。第三核心人员成为瓶颈。理论跟着人走走的不是流程和产出而是理解。第四团队协作变成猜谜。新人无法清空提问因为老员工自己也说不清只能用“这是历史原因”搪塞。这些后果在一个大型系统里是灾难性的。如果我们承认“理论”是唯一的真知识那知识管理就成了玄学。你无法把“理论”放进 Git 仓库也无法在离职交接时打包带走。很多组织最终只能靠“把核心开发者留下来”来硬撑但这既不可持续也不负责任。3.3 但需要承认Naur 的表述有现实依据我们得公平一点。Naur 的经验判断可能来自他观察到大量“照文档复制代码”的现象文档说得头头是道但程序员并没有真正理解程序所以一出问题就瘫痪。他说“理论无法完整表达”其实是在强调理解需要亲历而不是要否定所有外部表达。问题不在于 Naur 观察到的事实而在于他把“理论”和“外部表达”之间的裂缝叙述得太绝对仿佛理论是一个高不可攀的私人心智世界。这种叙述被后来的“英雄程序员”和“代码自闭”文化拿去当了挡箭牌。一个维护困难的系统最舒服的辩护词就是“你们不理解我的理论。”但这句话并不能让系统变得更好只会让系统变得更难触碰。所以我更倾向于把 Naur 的命题读成“理论无法通过文本完整传递”而不是“理论无法通过任何方式共享”。文本不够我们还有对话、演示、结对、评审还有共同修改时的集体试错。这些过程不依赖某个人的私有幽灵它们建立在可观察的行为之上。4. 把“理论构建”拉回到行动和协作中4.1 重新定义理论不是脑中之物而是团队行动模式我们可以从 Ryle 出发给 Naur 做一个修正关于系统的“理论”不在任何单个程序员的脑中而体现在团队如何讨论、修改、验证和演进这个系统的模式中。理解程序不是抽象的心灵状态而是能够采取一系列正确的行动。这些行动必须能被观察也必须有载体。这意味着理论并不是不可言说的。它当然不能一次说尽但它可以通过很多方式逐步外显设计决策记录、边界测试、命名、模块化、可运行示例甚至是团队处理变更时的对话习惯。每一样都是理论的切片。举个例子一个描述订单支付的模块如果团队能随口说出“这里为什么不用事务因为跨服务调用不能依赖本地事务”这句话本身就是一个理论切片。它虽然不完整但能够帮助新人绕过巨大的坑。把这些切片持续沉淀下来就是在维护团队理论。4.2 用“最小理解单元”代替“完整理论传递”工程上不存在一种能把所有“理论”从一个人传给另一个人的机制。真实有效的方法是不断让新成员重新构建理论然后通过反馈检验。具体做法可以分三层。个人层面要求新人从一个小任务开始亲手改代码、跑测试、查历史提交主动重建相关理论。不要只给新人一堆文档让他读文档只是索引真正的理论要在代码和变更记录里重新长出来。配对层面由老成员结对解释“为什么这里要这样”而不是只讲“这是什么”。把隐含判断变成对话。对话是理论共享效率最高的方式因为它允许对方质疑质疑会逼出更多隐藏假设。团队层面把关键设计决策整理成轻量标记ADR架构决策记录、变更理由注释、测试场景描述让未来的“理论重建者”有路径可循。这不是要写大部头文档而是在每个关键变更旁边留下一个“为什么不是其他方案”的便签。4.3 一份可执行的“理论共享”检查清单如果你正在维护一个“失智”老项目不要先提“重写”建议先做一次理论健康度检查。下面是常见信号和对应动作。信号可能原因第一步行动新成员总要问“为什么要这样”设计理由没有记录补 ADR先记录最近3个决策改一个 bug 会引发另一个 bug边界和依赖关系不清晰写测试尤其是描述边界的测试老员工代码评审只看格式理论只停留在个人脑海评审增加“这个改动背后的假设”环节文档描述了流程但没人信文档与真实代码脱节删掉大部分无关“理论”的文档保留行为用例离职交接时只能写一堆密码和路径代码里的意图注释缺失对高复杂度代码加“为什么不用更简单方案”的注释这一步不要贪大求全。把“理论共享”当成一个持续的习惯而不是一次知识管理项目。我见过最有效的团队并没有复杂的 wiki 系统只是在每个 PR 描述里多写了一段“为什么这么做之前试过什么为什么不行”。三个月后那个代码库的理论密度明显比以前高了。4.4 “理论断裂”的排查链条如果你进入一个陌生系统并且想快速重建一套可用理论我建议按这个顺序排查而不是一开始就扎进代码细节里。先看历史变更。找到最近三个月的高频改动文件这些文件往往承载着当前系统最活跃的理论。再看提交消息和 PR 描述尤其是“why”开头的句子那是别人刻意留下的理论踪迹。然后看测试特别是那些描述边界条件和异常行为的测试它们会告诉你系统“不该做什么”。接着看设计文档和架构图但只把它们当作地图不要当作真相因为地图很容易过时。最后找一个老同事带着具体问题去聊半小时。对话能补上所有文本遗漏的动态背景。这条链路的核心是先定位理论热点再寻找外显痕迹最后用对话验证。不要试图一次性理解整个系统而是围绕你即将修改的那个功能建立一块“局部理论”够用就好。5. 当“理论构建”遇上规模化边界与误区5.1 适用边界不是所有开发都需要“理论”我必须强调Naur 的“编程作为理论构建”并不适用于所有场景。如果你写一个一次性数据迁移脚本或者一个快速原型理论好不好不是核心问题代码能跑就行。这些场景下文本就是全部不需要、也不值得投入大量精力去维护“理论”。真正需要“理论构建”视角的是那些要长期演进、多人协作、复杂领域逻辑的系统。因为只有这类系统才会面临“理论断裂”后的巨大成本。判断标准很简单如果这个程序未来三年还会被修改那理解它的理论就比多写一点代码更重要。我见过不少团队把“编程是理论构建”当成万能药为一个小工具写长篇 ADR为每个常量加哲学注释结果文档比代码还难维护。理论构建是有成本的它的投资回报率取决于系统未来的变更频率。对一次性脚本最佳策略是“写完即弃”对核心服务最佳策略才是“理论优先”。5.2 别让“理论不可说”成为偷懒借口一个很常见的反面模式是用“编程是理论构建文档写不清楚”来拒绝写文档、拒绝做评审、拒绝回答新人提问。这不是 Naur 的本意但从他的论述里确实可以推导出这种态度。我的建议是你可以说“理论不能完全写下来”但你不能认为“理论不值得写下来”。恰恰因为理论无法一次说清我们才需要反复用代码、测试、文档、对话去逼近它。每一次外部表达都是在给未来的“理论重建”铺路。你要警惕两种声音。一种是“代码就是文档注释没有用”另一种是“文档必须完整覆盖所有知识”。这两种声音都会让知识管理走向极端。真实世界需要的是“足够好的文档”“标注意图的注释”“记录关键决策的 ADR”“描述行为而非实现的测试”。这些不是低配而是理论外显的最小稳定版本。5.3 从“个人天才”转向“团队记忆”Naur 的理论构建模型本质上仍然假设一个孤独的开发者面对一个系统。但今天的软件几乎都是团队产物。团队记忆比个人天才更值得管理。一个可行的做法是把“理论构建”变成团队流程的一部分每次设计评审不只看方案还记录“我们否掉了什么、为什么”每次代码评审不只看正确性还追问意图每次重构不只看输出结果还对比前后结构。这些做法听起来不如“构建理论”那么有哲学感但它们才是让理论真正活下去的日常操作。团队记忆还可以靠轮换机制加固。如果同一个模块总是一个人维护理论会逐渐变成私有资产。试着在迭代中让不同成员互相跨模块修改再安排一次复盘你发现了哪些原本不知道的假设把这些问题和答案留下来就是一次理论补充。6. 回到源头从“脑中的理论”到“行动中的理解”6.1 用 Ryle 修正 Naur绕了一大圈我们可以回到标题Naur 从《心的概念》里到底哪里搞错了我认为他错在把 Ryle 已经打碎的那个“内部幽灵”重新请回了大脑皮层。Ryle 说“知道如何”体现在行动中而不是隐藏于心智中。Naur 把编程理论说得太像一种不可言传的私人直觉这反而背离了 Ryle 对行为分析的坚持。如果坚持 Ryle 的立场那么一个团队对系统的理解就应该体现在他们能提出什么问题、能做出什么修改、能解释什么错误、能设计什么测试里。换句话说理解不是一个“拥有”的东西而是一组“能够做到”的行为模式。Naur 把理论构建作为编程的本体这个判断是对的但他把理论放置的位置是错的。6.2 理解是可观察的也是可培养的作为工程师我们不需要去猜别人脑中是否掌握理论。我们可以观察他的行为他能在一个新需求面前提出正确的约束问题吗他能预见到某个改动会破坏哪个隐藏依赖吗他能把复杂问题拆成可验证的小步骤吗这些行为才是“理论”的可见形态。同样我们可以通过练习来培养这些行为阅读代码时写微型设计笔记修改代码时先列举预期影响评审代码时主动寻找反例。这些动作会帮助我们把“理论构建”从私人领域带到公共空间让理解变成可以互相学习的技能。这也是我为什么认为 Naur 的论文仍然值得读。它提醒我们真正的软件维护困难不是“代码读不懂”而是“理论已经断了”。如果只盯着代码你会陷入局部修复的困局如果你意识到自己在重建理论你就会主动去寻找被忽略的历史、语义和意图。6.3 真正的传承是让下一代重新构建Naur 有一句话说得很对理解程序最终只能通过重新构建理论来实现。这句话的正确读法不是“只有原作者能理解”而是“要理解就必须主动重建”。团队的责任不是把理论塞进某人的脑子里而是为新成员提供足够的材料让他能更快地重建出与原作者相近的理论。所以比起试图把“理论”从一个人复制给另一个人我更愿意把精力花在如何让下一代人能够高效地重建它。这意味着我们要把那些“看不见但知道”的东西尽可能变成看得见的痕迹——哪怕只是一条注释、一张图、一次对话记录。每一条痕迹都是一次重新思考的起点。这就是我对标题的最终回答Naur 从《心的概念》里真正搞错的不是“编程需要理论”这件事而是“理论只能存在于个人头脑中”这个暗示。把理论拉回行动和协作它才真正可维护、可演进、可传承。
返回列表