作者:Stephan Schmidt

原文链接:Radical Simplicity in Technology

我是前CTO和软件开发者,目前正在以极简主义的理念开发 Inkmi – CTO Dream Jobs。

作为开发者,我们热衷于复杂性。我们通过使用 SPA(单页应用)、Vue/React、转译工具(如 TypeScript、Babel、Webpack、PureCSS)、GraphQL、JSON 等前端技术,以及后端的微服务架构、协议缓冲(Protobuf)、Kafka、InfluxDB 或各种 NoSQL 数据库来制造复杂度。这种复杂性是不必要的,并不在问题领域之内。它拖慢了我们的开发速度,让开发过程变得乏味。这种复杂性导致领域知识变得浅薄。极简原则让开发过程重新变得快速而充满乐趣。

这种复杂性需要一个解决方案来管理——通常是 Kubernetes。我们还需要部署这些服务,当微服务数量增多时,需要更多的服务器来运行它们,于是人们转向使用 AWS 云服务。但这也带来了新的复杂性。现在我们需要前端工程师来编写 React 应用,后端工程师来开发 REST 和 GraphQL 接口,还有运维工程师来管理 Kubernetes。这导致了复杂的测试环境:需要使用模拟对象和本地数据库、Docker 镜像,还要运行耗时数十分钟的构建流水线。不久之后,开发就会被这张复杂的网所束缚。

在过去的二十年里,我们的系统从最初简单可行的状态演变为如今异常复杂的庞然大物。 网页应用近年来变得极其复杂。

要做一个简单的事情也要付出很多时间,比如在表单中添加一个字段——例如为个人资料添加出生日期字段。本不该这么久?但实际上通常却是这样:原本几分钟就能完成的事情往往需要几个小时。接着各种东西出错,你得用整整一天时间让你的纸牌屋重新运行起来——在各个服务、每个框架和工具的各种版本之间谨慎地平衡。

结果就是开发者最少(且往往最后)才做的是业务逻辑的编写。如果你统计一下代码行数,就会发现真正的业务逻辑(代码中的各种 if 语句)所占的行数最少。而大部分事情都是进行数据的序列化:从 SQL 数据库读取数据,转换为对象,再转换为通过网络传输的 JSON,加载到 React 的数据存储,然后呈现到用户界面上,以至于需要在 GraphQL、SQL、JavaScript 对象和 Python 对象中重复定义同样的数据。这导致领域模型变得极其浅薄,应用程序也显得表层化。但我们真正想要的,是开发具有深层领域模型的应用。

极简应用程序拥有深厚的领域模型,而框架中的代码很少。

为什么我们要构建复杂的系统和架构?复杂的架构往往是在替代真正需要解决的问题。因为我们渴望挑战,而领域本身很浅薄,开发者就用新的框架和系统来自造挑战。他们想尝试新鲜事物,并且往往想把技术扩展到几年后(甚至可能永远不会达到的)那样的用户规模。过早扩展是初创公司失败的最大原因之一。

由此产生的问题种类繁多。正如 Rachel Kroll 写道:“代码在人的脑海中运行,请保持它的简单。”组件和系统也遵循同样的原则。在代码运行到计算机之前,它首先要在你的大脑中“运行”。

“如果需要花费一小时才能弄清楚发生了什么,那么这整整一小时就没法去做其他更有用、更有趣的事情。”

此外,技术团队花了大量时间处理自身的复杂性,而不是创造业务价值。管理这么多系统和组件需要专业知识,由于存在许多边缘情况,也会产生大量的 Bug。综合起来,这会导致效率低下,需要过多的开发者,并且成本高昂。

极简原则登场

什么是极简原则?它意味着使用尽可能少的组件和活动部件。重复使用已有的技术来满足不同需求,而不是为每个需求都引入新模块。例如,与其使用 Postgres 做数据库、Druid 做事件存储、Redis 做缓存、RabbitMQ 做消息队列、Elasticsearch 做全文检索,不如使用一个托管的 Postgres 同时作为数据库、全文搜索、HTML 缓存和发布/订阅系统,再配合 TimescaleDB 作为事件存储。这样可以让我们掌握更深入的知识,加快开发速度,让新开发者更快上手;减少潜在故障点,无需为几十种框架和组件做升级规划;同时提升开发者的满意度。开发的核心是进入“心流”状态。开发者处于心流状态时效率比非心流状态时高得多。频繁更换组件、升级框架或去查阅数据库各种边缘情况,都会打断这种心流。

Basecamp —— Rails 的创造者,践行了极简原则。他们使用纯 HTML(没有 React)开发了 Hey 邮件应用。Stack Overflow 也遵循了这一原则:他们的服务只运行在少量实体服务器上。有意思的是,所有 AWS 相关的问题都是由部署在实体硬件上的 Microsoft SQL Server 回答的。在 Stack Overflow 上,他们没有使用 GraphQL、React、Kafka、Webpack,也没有使用 Kubernetes。

微服务架构与实际所需对比。

你也可以采用极简原则。极简原则意味着不使用微服务,而是采用输出 HTML 的单体架构,前端只使用 Hotwire Turbo 和极少量的 JavaScript。仅使用一个托管的 Postgres 提供数据库功能,包括数据存储、JSON 存储、任务处理和消息队列,并使用列式存储(如 TimescaleDB)作为数据湖和数据仓库。如果你实在无法抗拒新技术,那么再加一个 Redis 用于缓存,因为 Redis 几乎不会出故障。

许多只有少量客户的创业公司,却同时运行着多个微服务,以及 Redis、Postgres、Elasticsearch、Kubernetes、Webpack、一个 JavaScript 单页应用 (SPA),还有 REST API、Apollo GraphQL,以及 Kafka 或 RabbitMQ 这样的消息队列/作业服务器。相比之下,一个极简化的架构仅使用一个托管的 Postgres(代替独立的 Postgres、Redis 和 Elasticsearch),在单体应用中使用 Unpoly 渲染服务器端的 HTML,并使用 BigQuery 作为分析型数据仓库。这样的架构规模更小,却能实现同样的功能,需要维护、学习和调试的部件要少得多。需要监控、添加到日志系统和创建告警的组件数量也大大减少。当拥有 50 多名开发者和数百万用户的公司可能需要这种复杂架构时,对大多数公司(尤其是在创业初期)来说,是否需要如此复杂的架构呢?答案是否定的。

标准配置 极简配置
多个微服务 单体应用
Vue Unpoly
Nuxt
Webpack
Babel
JavaScript 单页应用 (SPA)
REST API
GraphQL
Postgres Postgres
Redis
Elasticsearch
Kubernetes

当需求增长时,可以慢慢添加新的组件。质疑自己:真的需要那个新组件吗?极简原则意味着优先扩展现有的技术。例如,如果需要为原生移动应用提供 REST/GraphQL 接口,可以使用 Hasura 从 Postgres 数据库自动创建 REST/GraphQL 服务。

极简原则与“选择乏味技术” 朝着同一个方向努力:降低风险,提高效率。虽然“选择乏味技术”原则也涉及组件的数量(“如果每种技术的成本都很高,那么你应该只选少数几种”),但它主要强调选择经过验证的成熟技术。将极简原则与“选择乏味技术”结合,可以达到最佳效果。极简原则在现有成熟技术上也行得通——前提是你有足够的资金去雇佣精通这些技术的工程师;但更简单的做法是使用 Postgres 代替某个新奇的数据库,用 PHP 代替那些奇怪的新函数式语言。

极简原则还有许多其他积极的副作用:功能交付更快,创业者用更少的开发者就能完成更多任务,公司行动更快,更容易适应变化并迅速转型。极简原则可以大幅提高你的初创公司成功的概率,而不是失败率。如果你是创始人,一定要坚持极简原则

作为开发者,你能热爱极简原则吗? 这难道不意味着要放弃新技术吗?而正是新技术最初吸引你进入编程领域。我认为真正让你热爱编程的,是解决问题带来的挑战,而挑战的来源应该是解决深层次的业务难题,而非学习新的技术。有了更多时间,你就能用新的算法和深度功能来解决领域中的难题,让用户为之惊叹,而不是仅仅实现那些表面功能,比如把表单数据存入数据库中。

你能践行极简原则吗?当然可以! 作为创始人,你需要监督好你的 CTO,否则他/她会构建一个技术的“梦想城堡”。你已经在实践精益创业方法,以最大化公司成功的机会。极简原则在技术层面与精益创业理念完美契合,再加上 Scrum 的流程,共同组成了三位一体。它让你能够快速试验、提前交付,并在必要时迅速改变方向。而臃肿的架构则会拖累你,破坏你的精益创业努力。

作为 CTO,你需要营造一个环境,让团队在不依赖不断扩张的技术“工具动物园”的情况下成长和尝试。相反,你应该从业务中发现挑战,用你的创意、智慧和算法来解决,而不是依赖架子上更多的技术新玩具。我最自豪的编程时刻之一是:小时候自己设计算出汉诺塔问题的算法,以及在 Lucene 等框架出现之前就自己实现全文检索功能。Google 搜索的用户界面很简单,但其领域模型却非常深。当你在 Google 搜索中输入“2+2”时,它并不会搜索包含“2+2”这个词条的网页,而是直接显示一个计算器并给出结果 4。这是一个让用户惊喜的深度功能。这完全可以实现,你也可以做到!

极简原则抛弃了一切无法带来客户价值的技术。它造就了深层的领域模型和深厚的技术底蕴,带来更高的质量,让每个人都更快乐。极简原则让新开发者更容易理解系统,让环境搭建和测试变得简单,并让你重新掌握一切。极简原则必将胜利。

关于 Stephan Schmidt

Stephan 曾任多家初创公司的首席技术官(CTO)、临时 CTO 以及 CTO 教练,同时也是资深开发者。在高速成长的初创公司中观察到的那些复杂技术环境促成了极简原则的诞生,因为他见过太多成本高昂却问题频出的系统。1981 年左右,他在一家百货商店自学编程,使用 VIC20 的 BASIC 语言写代码。此后,他在多种环境下用多种编程语言编写并出售过代码,使用的语言包括:C64 BASIC、6502 汇编、CPC BASIC、Z80 汇编、68000 汇编、Amiga BASIC、GFA BASIC、Blitz BASIC、QBasic、Turbo Pascal、Modula-2、Oberon、Delphi、C、C++、Lisp、Prolog、Perl、Python、Java、JavaScript、Scala、Erlang、Haskell、TypeScript、Go 和 Rust 等。他使用过的硬件包括:VIC20、Sinclair 系列、C64、Sharp 掌机、CPC、Amiga、BeBox、Atari ST、MS-DOS 个人电脑、Windows 个人电脑、Linux(未发行版)机器、Sun 工作站、SGI 机器、NeXT Cube,以及各种型号的 Mac(Quadra、iMac、MacCube、MacBook、MacBook Pro、iMac Pro 等)。

Stephan 创办过几家初创公司,并曾在不同规模的公司中担任 CTO。在出售了最近的一家初创公司后,他开始从事 CTO 教练工作。Stephan 还撰写过一本名为《CTO Book》的书籍。你可以在 LinkedIn 上找到他的资料。