乐于分享
好东西不私藏

软件开发的本质:确定性叠加

软件开发的本质:确定性叠加
一个软件系统,本质上是一层层确定性叠加起来的结果。前一个功能点为后一个功能点提供基础,后一个功能点在前者的基础上继续构建。
如果前一个功能点的确定性还没夯实,就急着往上堆新功能,系统就会进入一种“脆弱平衡”——看起来在运转,实际上随时可能崩塌。

一、从产品到功能:先剪枝,再生长

把一个模糊的产品想法,拆解成具体的功能点,很像修剪一棵树。产品愿景是树干,功能模块是树枝,具体功能点是树叶。你要做的,是判断哪些树枝该留、哪些该砍。
每一次“剪枝”,都是一次确定性决策。
比如说,你要做一个在线教育平台。你可以选择先做“课程播放”和“用户注册”,也可以选择先做“个性化推荐”和“社交互动”。前者确定性高、风险低,后者不确定性高、投入大。
这个选择没有绝对的对错,但它决定了你后续所有工作的基础。
人的角色,就是在这个阶段做出价值判断和风险决策。哪些功能点是其他功能点的前提?哪些可以往后放?哪些干脆砍掉不做?

二、功能点之间:依赖关系决定叠加顺序

这里将功能点之间的关系分成三种:
  • 第一种:强依赖。B必须在A之后做,因为B直接用了A的东西。
  • 第二种:弱依赖。A和B可以同时做,但最后要对得上。
  • 第三种:无依赖。两个功能完全不相关,谁先谁后无所谓。
理解了这三种关系,你就知道了什么时候该串行、什么时候该并行。一个务实的原则是:强依赖必须串行,弱依赖可以并行但要定期对齐,无依赖随便并行。
“先做后做”不仅是排期问题,也是确定性叠加的问题。
如果你在一个强依赖关系中,上游功能还没稳定就开始做下游,那你等于在给自己埋雷。下游功能写完了,上游改了接口,下游全得重写。

三、功能点内部:代码层面的确定性从哪里来?

通过设计、编码和测试,一步步缩小“意外”的空间,夯实确定性。
  • 设计的确定性:在写代码之前,先把方案想清楚。接口长什么样?数据怎么存?异常怎么处理?这些问题在写代码之前定下来,就是设计阶段的确定性。
  • 编码的确定性:写代码的时候,用明确的逻辑控制程序走向。if/else 的每个分支都要覆盖,不能留“其他情况再说”。选合适的数据结构,用类型系统约束变量范围。这些都是让代码行为可预测的手段。
  • 测试的确定性:写完代码,用测试用例验证它是不是真的按照预期工作。测试本身就是一种确定性声明——它告诉所有人,这段代码在什么情况下应该有什么表现。
这三个环节加起来,就是一个功能点从模糊到确定的全过程
当然,这不意味着一个功能点要做到完美无缺才能往下走。更现实的标准是:核心逻辑通过了测试,关键边界情况被处理了,对外接口相对稳定,没有留下严重的技术债。
“够用”就好,不必追求“完美”。

四、AI Coding 时代:确定性叠加的新挑战

AI 生成代码的能力很强,但它有一个特点:它倾向于给你一个“看起来差不多”的答案,而不是一个“确定正确”的答案。
如果你给 AI 一个很模糊的指令,比如“写一个电商网站”,它会在海量的可能性中随机组合,结果往往漏洞百出。这就相当于你把前面说的“剪枝”工作也交给了 AI——而它根本不了解你的业务场景。
正确的用法是:你来完成高层次的确定性分解,把边界清晰的小任务交给 AI。
比如,你不要让它写“整个购物车模块”,而是让它写“一个 Python 函数,输入商品 ID 列表,从 MySQL 的 products 表中查询名称、价格和库存,返回字典列表。”
这样做的结果是:AI 生成的代码大概率是可用的,你只需要做少量的检查和微调。
换句话说,AI 时代,开发者的核心能力不再是写每一行代码,而是把一个复杂问题拆解成一系列边界清晰、确定性高的子任务。
谁能把这个拆解得更好,谁就能更高效地用好 AI。

五、一个完整的确定性叠加模型

最后,我把前面的内容串起来,形成一个完整的框架:
  • 宏观层面(产品):通过树形分解和价值判断,把模糊的产品愿景拆成一组有依赖关系的功能点。
  • 中观层面(功能点之间):识别依赖关系类型,强依赖串行、弱依赖并行但对齐、无依赖随意并行。
  • 微观层面(功能点内部):通过设计、编码、测试三个环节,让一个功能点的行为变得可预测。
  • AI 赋能层面:人类更多负责前三层的确定性分解(当然,也可以利用AI加速沉淀前三层的确定性),把边界清晰的任务交给 AI,再通过验证(保持传统的CI 和 CD,也包括当前的 Harness 等技术)将其输出整合到系统中。