很多系统的问题,不是代码写得不够努力,而是规则没有被放在正确的位置。
团队说”我们约定这样做”,上线后变成口口相传;接口说”正常情况会返回这个字段”,异常路径却没人定义;流程说”审批通过后再执行”,真正出问题时才发现状态机根本没有画清楚。
协议化思维要解决的不是某个具体框架,而是一个更底层的问题:把不确定性前置,并让它可以被验证。
文档不是协议
文档能解释意图,但文档本身不会约束系统。
真正的协议至少包含四件事:
- 参与方:谁可以发起动作,谁负责验证,谁拥有最终解释权。
- 状态:系统有哪些合法状态,状态之间如何迁移。
- 消息:每一次交互的输入、输出、幂等语义和版本兼容策略。
- 失败路径:超时、重试、冲突、回滚、降级分别怎么处理。
如果这些东西只存在于会议纪要里,它们就不是协议,只是愿望。
边界越模糊,信任成本越高
一个常见的坏味道是:系统边界靠人来补。
比如订单服务把库存扣减、支付确认、发货通知都串在一个同步流程里。只要某个环节抖动,整条链路就卡住。更麻烦的是,失败后到底谁负责补偿,经常要靠人肉查日志。
更好的做法是把边界说清楚:
- 订单只承诺自己的状态迁移。
- 库存只承诺扣减请求的幂等处理。
- 支付只承诺确认事件的可追踪投递。
- 发货只消费已经完成的业务事实,而不是猜测上游意图。
系统之间不需要互相信任”你应该会处理好”,它们只需要相信彼此暴露出来的协议。
状态机是最朴素的严肃性
很多复杂业务最后都能落到状态机上。
状态机看起来不高级,但它有一种非常实用的力量:让模糊的业务讨论变成可枚举的问题。
例如一个发布流程可以被拆成:
draft -> reviewing -> approved -> scheduled -> published
-> rejected
接下来问题就清楚了:
- 哪些角色可以触发迁移?
- 哪些迁移允许撤回?
- 重复提交会发生什么?
- 审核通过后内容还能不能改?
- 定时发布失败后进入什么状态?
这些问题如果不上桌,最后一定会以线上事故的形式上桌。区别只是你想在白板上处理,还是在凌晨处理。
验证要靠系统,不靠默契
协议化设计的最后一步,是把规则变成可验证的东西。
在工程里,这可以是:
- OpenAPI / JSON Schema 这类接口契约。
- 数据库约束、唯一索引和外键边界。
- 事件版本号与消费者兼容测试。
- 状态迁移表和单元测试。
- 可观测性里的 trace id、事件日志和审计记录。
验证不是为了显得规范,而是为了降低协作成本。系统越大,人的记忆越不可靠;参与方越多,“大家都知道”就越危险。
最后
协议化思维的核心不是冷冰冰的形式主义,而是对复杂系统保持诚实。
它承认失败一定会发生,边界一定会被误解,状态一定会走到奇怪角落。所以它提前把规则写清楚,把异常路径留出来,把验证机制接上去。
一个好协议的价值,不在于它让系统永远不出错,而在于出错时大家知道自己站在哪里、能做什么、下一步该怎么恢复。
这已经是工程世界里相当高级的温柔了。