“我们不想追求完美。” “我们不想构建尽善尽美的解决方案。”——类似的话我们听过太多,以至于“完美”这个词几乎成了某种忌讳。这种谨慎当然有道理:过度设计确实可能拖垮团队,不少人已经将任何看似完美的事物都等同于同样的风险。

但事实并非如此。业界其实悄悄把两者混为一谈了。
过度设计,本质上是解决了错误的问题。这就是它最完整的定义。不是“过度关注”,也不是“做得太好”。就是——解决错了问题。出发点固然是好的,却几乎总是伴随着越来越复杂的附带成本。
存在一个完美的解决方案
那么,是否存在一个完美的解决方案?答案是肯定的。但有一个重要的前提:你需要一套非常明确的需求——把每一项约束都摆在桌面上。当这些约束拧得足够紧时,有趣的事情就发生了:最终只会剩下一种可能的解决方案。有点讽刺,但这正是完美的方案——因为它是唯一适合的。
举个例子:一个新项目,各种语言、各种工具、各种托管模型都摆在面前。你选择了无服务器架构,Python 是一个强有力的候选:无需编译,上传文件到 Lambda 就能发布。但对其他人来说,这可能是错误的选择——他们不熟悉 Python,或者他们的需求对性能有更高要求。不同的约束,不同的答案。相同的问题空间,不同的“完美”。
再比如,你选了 Python,要构建一个 Web 应用。Django 还是 Flask?两者都能实现类似的结果,但理念完全不同。哪个胜出?取决于更明确的需求、更严格的约束。一旦设定清楚,解决方案自然浮现——对当前场景来说,那就是最适合你的方案。
系统即产品
当系统被过度设计时,原因几乎总是出在需求上。这里说的需求,是产品意义上的需求,而不只是技术层面。
一个库、一个 API、一个内部工具……我们总喜欢假装这些是“纯粹的技术”,好像它们不属于产品的范畴。但事实并非如此。它们都有用户,这些用户有各自的需求。你需要充分理解这些需求,才能正确满足它们。
也许用户需要的只是一个服务,或者一个库比 HTTP 调用更合适。也许你给他们的不该是一个 API,而是一个包。只有当你把系统当作产品,并且诚实地定义需求时,解决方案的形状才会变得清晰。然后,解决方案自然就跟着来了。
你怎么知道
判断某样东西是否过度设计,最明显的标志是:你开始问“为什么这么建?”而答案根本站不住脚。
一个经典例子:一个三人团队维护着五个微服务,这些服务之间互相共享数据。这是过度设计吗?要判断,就找出他们试图解决哪个问题。很可能你会得出结论:他们解决的是错误的问题(或者同时解决了好几个)。
看看分割带来的实际成本。原本是数据库中的硬引用,引擎帮你强制执行的外键,现在变成了字段里的松散字符串 ID。数据完整性消失了。一个服务可以删除一条记录,另一个服务却毫不知情——它只是保留了一个悬空的引用,直到后来用艰难的方式才发现。当这些服务都属于同一个域时,为什么还要搞这么多仪式?为什么要放弃这些完整性检查?
交换中得到了什么?通常,失去的比得到的多。独立部署?看上去很美,但三人维护一个域,你真正需要解决的是扩展和所有权问题吗?你为这个根本没摆上台面的问题,付出了分布式不一致、运营开销,以及一个只能部分解决多个问题(但一个都没完全解决)的系统,还顺带引入了一堆本来不会遇到的问题。
这就是过度设计的签名:不是优雅,不是彻底,也不是说这些方案不好——它们通常都是所提出问题的正确答案。问题是,这些问题是你不曾遇到过的。
收集正确的要求
所以诊断很简单,尽管处理起来并不简单。过度设计本质上是需求收集的失败——你可以称之为“产品工程”的失败。收集了错误的需求,然后针对这些错误需求去努力设计,结果就是过度设计。
完美从来都不是敌人。真正的敌人是模糊不清的需求。把需求做好,把所有约束都摆在桌面上,完美的解决方案就不再是幻想,而是唯一剩下的那个。
