选择无聊的技术:工程决策中的创新 Token 理论
在技术选型的讨论中,我们常常被新技术的光环所吸引。NodeJS、MongoDB、微服务、Kubernetes......每一项新技术都承诺解决我们面临的问题。然而,经过多年的工程实践,越来越多的工程师意识到:在大多数情况下,选择"无聊"的技术才是更明智的决策。
前 Etsy 工程师 Dan McKinley 的这篇经典文章,为我们提供了一个务实的技术选型框架。这不是保守主义,而是一种深思熟虑的工程智慧。
创新 Token:有限的注意力资源
McKinley 提出了一个精妙的比喻:设想每个公司拥有大约三个"创新 token" 。
这是你可以用来冒险的筹码,但供应量在很长时间内是固定的。你可能在达到一定的稳定和成熟后能获得更多,但人们通常会高估自己钱包里的余额。
当你做出以下选择时,你就在消耗这些珍贵的 token:
- 选择 NodeJS 构建网站?花掉一个 token
- 选择 MongoDB 作为数据库?又花掉一个 token
- 选择一个存在不到一年的服务发现技术?再花掉一个 token
- 如果你决定自己写数据库------那你的麻烦就大了
对于 JavaScript 咨询公司或数据库公司来说,这些选择可能是合理的。但你很可能不是。你可能在为一家"重新思考全球商务"或"重新发明网络支付"的公司工作,推进着某个宏大的商业使命。
在这个背景下,把有限的注意力用在创新基础设施技术上,是一种绝佳的失败方式,或者至少会延迟你的成功。
无聊技术的真正价值
首先需要明确:"无聊"不等于"糟糕"。
世界上确实存在既无聊又糟糕的技术,你应该避免它们。但也有大量无聊且优秀(或至少足够好)的技术选择:
- MySQL 是无聊的
- Postgres 是无聊的
- PHP 是无聊的
- Python 是无聊的
- Memcached 是无聊的
- Squid 是无聊的
- Cron 是无聊的
充分理解的失败模式
无聊技术的美妙之处在于:它们的能力被充分理解,更重要的是,它们的失败模式也被充分理解。
在选择技术时,你面对两类不确定性:
已知的未知(Known Unknowns)
"我们不知道这个数据库达到 100% CPU 时会发生什么。"
未知的未知(Unknown Unknowns)
"天哪,我们完全没想到写入统计数据会导致 GC 暂停。"
即便是存在了几十年的技术,这两个集合通常都不是空的。但对于新兴技术,未知的未知的规模要大得多------这至关重要。
当凌晨三点系统宕机时,你会深刻理解这一点的价值。对于成熟技术,你可以在 Stack Overflow 上找到答案,可以咨询有相同经验的同行,甚至可以找到详细记录失败案例的博客文章。而对于新技术,你可能是第一个遇到这个问题的人。
追求全局最优而非局部最优
向公司添加技术是有成本的。这个道理作为抽象陈述显而易见:如果我们已经在用 Ruby,再添加 Python 似乎不太合理,因为增加的复杂性会超过 Python 带来的边际效用。
但奇怪的是,当我们讨论 Scala vs Python 或 Redis vs MySQL 时,人们往往失去理智,抛弃所有约束,开始狂热地谈论"为任务选择最佳工具"(Best Tool for the Job)。
重新定义"最佳工具"
你的工作本质上是将业务问题映射到软件解决方案的空间中。如果软件选择真的没有任何包袱,你确实可以为各种问题挑选一堆局部最优的工具。
但在现实世界------那个运维是核心关切的世界------你需要进行全局优化。
"最佳工具"思维的问题在于,它对"最佳"和"任务"的理解过于狭隘:
- 你的真正任务不是"实现一个缓存层",而是"保持公司正常运转"
- 真正的"最佳"工具不是在某个单一维度上得分最高的,而是在尽可能多的问题上占据"最不差"位置的工具
保持系统可靠运行的长期成本,几乎总是远远超过你在构建时遇到的任何不便。成熟而富有成效的开发者理解这一点。
运维负担的复合效应
考虑以下场景:
- 你的团队使用 5 种不同的编程语言
- 3 种不同的数据库系统
- 4 种不同的消息队列
- 每种技术都有自己的监控工具、日志格式、部署流程
当系统出现问题时,工程师需要在这些技术栈之间跳转。新员工的学习曲线变得陡峭。招聘变得困难,因为你需要找到精通这个独特技术组合的人。
复杂性不是线性增长的,而是组合爆炸式增长的。
何时以及如何选择新技术
把这种推理推向极端------比如只选 Java 并试图仅用它实现整个网站------显然是疯狂的。你需要某种向工具箱添加新东西的机制。
关键的第一步是承认:这是一个过程,也是一场对话。新技术最终会产生公司层面的影响,因此添加技术是一个需要全公司可见度的决策。
评估新技术的实践框架
第一步:如何用现有工具解决问题?
这是最重要的问题,能有效检测出"问题"是否只是某人想尝试新技术的借口。如果是这样,应该立即否决。
你会惊讶地发现,有限的技术选择集合能走多远。实践中,答案几乎从来不是"我们做不到",通常只是某种程度的"我们可以做到,但会比较困难"。
如果你认为现有工具无法实现目标,你可能只是思考得不够有创造力。
这不是说要强行使用不合适的工具。而是说,在引入新的复杂性之前,确保你已经充分探索了现有工具的可能性。
第二步:明确记录现有方案的困难
写下用当前技术解决问题的具体困难和成本:
- 开发时间会增加多少?
- 性能会差到什么程度?
- 维护成本如何?
- 是否存在根本性的技术限制?
这个过程有两个好处:
- 迫使你量化"困难",而不是停留在模糊的感觉上
- 为未来的决策提供了可对比的基准
第三步:设定迁移预期
新技术选择可能是纯增量式的(比如"我们还没有缓存,所以添加 Memcached")。但如果它与现有工具重叠或替代现有功能,你应该为迁移设定明确的期望。
策略通常应该是"我们承诺迁移"并附带建议的时间线。
这一步的目的是:
- 将技术债务保持在可控水平
- 避免局部最优解的泛滥
- 防止系统变成"技术动物园"
决策流程不应繁琐
这个过程并不需要繁文缛节------只是几个填空题作为作业,然后开会讨论:
- 我们试图解决什么问题?
- 用现有技术解决有什么困难? (具体量化)
- 新技术如何解决这些困难?
- 引入新技术的成本是什么? (学习曲线、运维负担、招聘难度)
- 如果新技术与现有工具重叠,迁移计划是什么?
如果一项新技术能通过这些挑战,那么引入它是合理的。
多语言编程的陷阱
多语言编程(Polyglot Programming)常被包装成一个诱人的承诺:让开发者完全自由地选择工具,会使他们更有效地解决问题。
这种说法往好了说是天真,往坏了说是自我欺骗。
理想与现实的差距
理想情况:每个团队选择最适合他们问题域的语言和工具,各自高效地交付价值。
现实情况:
- 跨团队协作变得困难:A 团队的 Go 服务需要调用 B 团队的 Scala 服务,双方对错误处理和超时的理解完全不同
- 共享库和工具无法复用:每种语言都需要自己的日志库、配置管理、服务发现客户端
- 运维团队被压垮:需要维护多套部署流程、监控系统、日志收集管道
- 人员流动成本高昂:工程师在团队间转岗需要重新学习整套技术栈
这种方式产生的日常运维负担会把你压垮。
真正的工程自由
有意识地选择技术给了工程师真正的自由:思考更大问题的自由。
当你不需要花时间:
- 为第五种编程语言配置 CI/CD 流程
- 调试不同技术栈之间的互操作问题
- 学习又一个新框架的特殊语法
你就有更多时间:
- 理解业务需求
- 设计更好的架构
- 优化用户体验
- 解决真正有价值的技术挑战
为技术而技术只是万金油,解决不了真正的业务挑战。
案例:Etsy 的技术栈演进
McKinley 在 Etsy 的经历是这个理念的最佳注脚。
Etsy 在很长一段时间内主要使用:
- PHP 作为主要的 Web 开发语言
- MySQL 作为主要数据库
- Memcached 作为缓存
- Gearman 作为任务队列
这些都是"无聊"的技术。但 Etsy 用它们支撑了一个处理数十亿美元交易的全球电商平台。
当 Etsy 确实引入新技术时(比如引入 Scala 用于搜索和推荐系统),这是经过深思熟虑的决策,针对的是现有技术栈确实难以解决的问题。
给技术领导者的建议
建立技术决策文化
- 使新技术引入成为可见的过程:不要让个别团队或开发者悄悄引入新技术
- 要求写下决策理由:强制进行上面提到的三步评估
- 定期审查技术栈:识别并清理没有充分使用的技术
- 设定迁移预期:新技术不应该只是"添加",还应该"替代"
培养工程师的判断力
年轻工程师往往被新技术吸引,这是自然的。但优秀的技术领导者应该帮助他们理解:
- 技术选择的长期影响:今天引入的技术,可能需要维护十年
- 全局优化的重要性:个人生产力 vs 团队生产力 vs 组织生产力
- 失败模式的价值:知道什么时候会出问题,比永远不出问题更重要
知道何时打破规则
这个框架不是教条。有时引入新技术是正确的选择:
- 现有技术确实存在根本性限制:比如需要实时数据处理,而批处理系统无法满足
- 新技术已经足够成熟:虽然对你的组织是新的,但在行业中已经被广泛验证
- 你有能力承担成本:团队有足够的资源学习和维护新技术
- 这是你的核心竞争力:如果你是一家搜索公司,投资最前沿的搜索技术是合理的
关键是:这应该是有意识的决策,而不是因为"这个技术很酷"或"我想在简历上写这个"。
结语:成熟不是保守
选择无聊的技术不是反对创新,而是关于智慧地分配有限的注意力资源。
在一个充满不确定性的世界中,减少不必要的复杂性是一种竞争优势。当你把创新 token 用在真正重要的地方------那些直接推动业务使命的创新上,而不是分散在基础设施的各个角落时,你的团队才能走得更远。
正如 McKinley 总结的:
"保持系统可靠运行的长期成本,几乎总是远远超过你在构建时遇到的任何不便。成熟而富有成效的开发者理解这一点。"
这不是保守主义,而是工程成熟度的标志。
当你的公司在凌晨三点没有宕机,当新员工能在一周内上手,当你的团队可以专注于解决业务问题而不是基础设施问题------你会感激当初选择了那些"无聊"的技术。
原文 :Dan McKinley, "Choose Boring Technology", 2015
链接 :mcfunley.com/choose-bori...
延伸阅读:
- "The Grug Brained Developer" - 另一篇关于简单性的经典文章
- "Stevey's Google Platforms Rant" - 关于平台思维的思考
- "Boring Technology Club" - 一个收集成熟技术栈的社区项目