能跑:为什么简单优先是最高原则
第1篇给了四阶段框架。这篇把「能跑」拆开——为什么简单优先在这个阶段是最高原则什么情况下它不适用以及怎么判断自己该进入下一阶段。一、为什么需要简单复杂度的真正代价复杂度的代价不是「写的时候费劲」是「改的时候绝望」。代码写一次被读几十次、改几十次。每多一层抽象、多一个中间件改的时候就要多排查一个可能的出错点。MVP 阶段需求必然变化——今天对的抽象明天就可能是错的。简单意味着改起来便宜。但这只说了一半。复杂的真正代价在于系统的不确定性你不知道用户会不会用、不知道数据量会多大、不知道瓶颈在哪里。每多引入一个组件就多一个可能出错的点就多一份调试时的茫然。简单不是偷懒——是在信息最少的时候用最少的东西降低意外发生的概率。更隐蔽的代价是复杂度会自我繁殖。一个抽象的引入会推动周围代码向它靠拢——上了消息队列上下游都要适配它的投递语义。这些不是一次性的决策是会持续影响后面每一个迭代的约束。在「不知道要做什么」的阶段引入这些约束等于在没看清路之前先把鞋带系死了。还有一个更根本的视角知道不做什么比知道要做什么更重要。你在「能跑」阶段做的每一个减法——不引入消息队列、不做抽象层、不上微服务——都是在对系统的不确定性说「我认」。你承认自己不知道用户会不会用、不知道瓶颈在哪所以你选择先不做。而过度设计的人不是在犯错是在用「万一需要」给自己壮胆万一流量大了呢上消息队列。万一查询复杂了呢做 DSL。万一需要扩展呢拆微服务。每一个「万一」听上去都有道理但把它们加在一起你得到的不是一个更稳健的系统是一个还没跑起来就被复杂度压垮的系统。二、如何避免复杂砍、硬、短砍砍到刚好能验证假设不是砍到最小是砍到「刚好够判断这个方向值不值得继续」。用户管理先用 admin 账号顶着。权限先不做。通知先不做。每多一个功能就多一个迷惑项——用户反馈好你不知道是因为核心价值还是因为那个附加的辅助功能。砍到只剩核心链路反馈才是干净的。怎么判断该不该砍问自己「这个功能不做核心假设还验证得了吗」。验证不了必须留。验证得了但可以让体验更好——砍下一个阶段再说。硬硬编码不是技术债是期权一个功能只在一种场景下用到就别为它建一套配置体系。一个参数只有一个值就别为它加环境变量。硬编码之所以被鄙视是因为在「跑得稳」阶段它确实会成为瓶颈。但在「能跑」阶段它是性价比最高的选择写死只需一行代码抽象需要一套配置、一个 loader、一个默认值、一个 fallback 逻辑。更重要的是——硬编码是期权。验证通过了再回头抽象你手里有真实的业务语义知道什么东西该配、什么东西不变、什么边界值会出问题。验证没通过整个功能直接扔掉一行重构代码都不用写。为了一个想象中的灵活性提前抽象你买的不是保险是一张不知道什么时候才能兑现的彩票。短代码短文件少依赖轻一个能跑的原型1000 行代码能搞定的事不要搞成 10 个微服务。每一行代码都是维护成本——不是这一周的成本是接下来每一周的成本。在「能跑」阶段标准是「能不用就不用」——不是反库是反不必要的库。一个 SQLite 能搞定的数据存储别上 Postgres一个单文件能跑的服务别拆成三个模块。三、如何权衡简单优先的边界简单优先不是绝对的教条。它在不同项目类型里权重不同0→1 探索型项目——你不知道用户会不会用、不知道核心假设对不对。这时候简单优先是最高原则复杂度是负债。做当下信息量下能做的选择不做「万一将来需要」的设计。确定性交付型项目——业务方把需求拆到字段级别数据量、并发量都有预估。这时候简单优先要让位于「该预留的扩展点」。但不是所有扩展点都该留——只留那些「业务方明确说过下个季度会做」的东西。为「万一」预留的扩展点依然是过度设计。技术验证型项目——目标不是上线是搞清楚一个技术能不能用。这时候简单优先依然适用但「砍」的标准变了不砍技术探索路径砍的是业务功能和边缘 case。先跑通一个最小闭环技术可行性被验证了就停下来。三种项目类型权重不一样但原则本身不变做当下信息量下最合理的决策。信息少的时候不瞎猜信息多的时候不偷懒。四、真实案例案例 1定时任务调度为什么不上 Airflow曾经做过一个定时任务调度系统。刚开始的时候觉得「以后任务会越来越多肯定需要一个成熟的调度平台」于是直接上了 Airflow。搭集群、配 DAG、搞监控前前后后花了不少时间。一年后回头看任务总共只有两个但 Airflow 本身从来没消停过——Scheduler 挂了、Worker 失联、元数据库锁表、版本升级不兼容每个月都要花时间处理。这个项目的核心价值是那两个任务产出的数据不是调度平台本身。Airflow 带来的不是「更可靠的调度」是「更多要维护的东西」。如果当时用 cron 一个 shell 脚本省下的不是搭建的那几周是之后一年里每个月的运维时间。在任务只有两个的时候Airflow 的复杂度不是在解决问题是在制造问题。案例 2一个业务数据处理系统配置抽象过头做过一个业务数据处理系统。一开始想得很大把几乎每个处理环节都做了配置抽取——数据源、清洗规则、输出格式、告警阈值全抽成配置文件想着「以后换任何一个环节都不用改代码」。结果配置文件越堆越多几十个 YAML每个都有 implicit default、环境覆盖、链式 fallback。一个参数最终用了哪个值要顺着三层 fallback 规则才能定位。出了问题不是查代码是查配置文件之间的 fallback 链。最讽刺的是线上跑了半年真正被「灵活切换」过的配置一个都没有——所有环节都用的是最初的默认值从来没变过。为了一个从来没被调用过的灵活性写了上千行约束校验代码。案例 3新产品验证把 MVP 做成了平台公司为了开拓一个新市场决定做一个面向新市场的实时数据产品。一开始雄心勃勃要做一个完整的实时处理框架从数据接入、实时计算、结果分发一条龙全自研。投入了大量人力从头搭建新框架功能做得非常强大覆盖场景非常多。两年过去了系统架构越来越复杂稳定性一直上不去而那个新市场——始终没有给出任何反馈。不是产品不好是市场本身就不存在。最后项目关停大量裁员。这不是执行力的问题是验证顺序的问题应该先用最简单的方案去市场里试一下确认有需求了再大投入。但他们把「验证」和「建设」的顺序颠倒了——先花两年把东西建好再去看有没有人要。到头来建的东西越复杂沉没成本越大越舍不得叫停越陷越深。三个案例的共同特征三个案例都在同一件事上栽了跟头在信息最少的时候用「确定性交付」的标准去要求一个「探索型」项目。Airflow 那个不知道任务量会不会涨就开始建平台配置那个不知道什么东西会变就把所有东西都抽成了配置——为了空想的灵活性引入了不必要的抽象结果抽象本身成了最大的维护负担新产品那个不知道市场存不存在就开始做全栈。每一个「万一」听上去都有道理但把它们加在一起你得到的不是一个更稳健的系统是一个还没跑起来就被复杂度拖垮的系统。不是「别想太多」——是「想太多想错了地方」。你该想的是核心假设怎么验证不该想的是技术架构怎么完美。五、退出信号什么时候该离开「能跑」阶段第1篇给过一个信号当你开始问「这样写对不对」而不是「能不能跑」。你在关心代码质量、命名规范、抽象合理性——说明核心链路已经跑通了你已经从「能不能跑」的焦虑里出来了。还有一个更具体的信号当你开始被同一个问题反复绊倒。一个参数忘了传、一个边界条件没处理——每次都是同样的问题。说明这个地方的「硬编码」开始产生真实的维护成本了该抽象了。但你是在被绊倒几次之后才动手不是第一遍就动手——区别在于现在的你有经验了知道这个抽象的边界在哪抽象出来的东西不会被下个月的现实打脸。两个信号都满足说明该进下一阶段了。缺一个继续砍、硬、短。收尾「能跑」阶段的简单优先不是在说「别想太多」——是在说「想对地方」。你该花时间想的不是技术架构完不完美是核心假设能不能被验证、怎么用最小的成本验证它。知道不做什么比知道要做什么重要得多。