速成课 · No. 02
不是一个头衔,不是一个职级,也不是一场考试的结果。而是一种看问题的眼光——今天的决策,塑造未来三年。
31 个概念 · 几乎每个都配一张图 · 结尾有速查表
架构师和工程师的区别在哪。不在于知识——而在于看问题的方式。
课程地图:八组共 31 个概念
一张地铁线路图:先看到全部线路,再一站一站地坐过去。
架构不是一堆技术——而是一组动手写代码之前要问的问题。每个问题在业内都有自己的名字:Trade-offs、Coupling、Idempotency、ADR。本课把它们归成八组——第 1–8 章依次讲这八组。
怎么读这门课
护照:每本的栏目都一样,所以容易比较。
每个概念都按同一套方式讲:它的规范名称(记住它——这是你要去搜索的词,也是面试里会被问到的词)、一个生活中的画面、一张图,以及一张带栏目的卡片:是什么、为什么、例子、陷阱(人们通常在哪里出错)和关联概念。每章以它的关键问题收尾;全课以检查清单和速查表收尾。
Problem Framing — 质疑任务,而不只是质疑方案
病人进门就要一张处方。好医生先问哪里疼——也许会发现,他点名要的那种药本来就开错了。
当有人说"做 X"时,工程师问"我该怎么做 X"。架构师问的是**"我们为什么需要 X,有没有别的东西能解决真正的问题"**。这就是 problem framing(界定问题)。任务常常本身就提错了——而某人点单要来的那个方案,一年后变成了问题本身。
- 是什么
- 在动手做方案之前,先找出请求背后真正的问题。
- 为什么
- 最贵的错误,是把错误的东西做得完美。
- 例子
有人让你"加一个 Excel 导出"。你问为什么——原来是大家觉得系统里的报表不好用。那就去改报表。
- 陷阱
- 不问"为什么",直接跳到"怎么做"。
- 关联
- 为 Trade-offs 提供选项;"什么都不做"往往就是 YAGNI 的答案。
Long-term Thinking — 以年为单位的视野,为后果负责
工程师为周六要搬进来的那家人盖房子。架构师则问:"等他们有了第三个孩子呢?等母亲搬来同住呢?等他们想把一楼出租呢?"
工程师解决被提出来的那个问题。架构师解决三年后它会变成的样子那个问题。这意味着:为还没用上的能力做设计,同时刻意舍弃那些听起来优雅、却永远用不上的能力。
木匠把钉子钉歪了,一天就能修好。架构师把承重墙放错了位置,房子修不了——只能将就着住。
所以架构师要为后果负责,而不是为代码负责。工程师的错误,一周就能修好。架构师的错误,公司要为它付好几年的账——付在技术债里、付在流失的团队里、付在错过的机会里。架构师必须在第一行代码写下之前就想清楚,并且有勇气在架构扛不住真实需求时叫停工程。
- 是什么
- 用一个决策在两三年后的样子来评判它。
- 为什么
- 架构错误最贵:要付好几年的账。
- 例子
- 选数据库表结构时,问一句:"客户多了十倍怎么办?再多出一种语言怎么办?"
- 陷阱
- 把"想在前面"和"提前全做好"混为一谈。想——要;多造——不要。
- 关联
- 由 Simplicity 和 YAGNI 来平衡;帮你判断 Reversibility。
工程师把事做对。架构师做对的事。
架构是选择放弃什么的艺术。不存在完美的方案。
Trade-offs — 每个决策都有得有失
自行车比走路快,但下雨会淋湿。汽车淋不着,但会堵在路上。地铁不堵,但不能送到家门口。
工程师选技术时,常常想找"最好的"。这是幻觉。架构师知道:任何选择都是在好几条轴线上的取舍——开发速度对可维护性,简单对灵活,现在的成本对三年后的成本。把轴线说出来、有意识地选择,就是架构工作。
- 是什么
- 每个选择都给你一些东西,也拿走一些东西。没有"最好"。
- 为什么
- 大声说出来的取舍,可以讨论、可以回头再看。藏起来的,不行。
- 例子
- 托管的云服务:起步快(得),但量大时更贵、也更难离开(失)。
- 陷阱
- 一味寻找"最好的技术",而不是合适的那个。
- 关联
- 记录在 ADR 里;今天有意识的损失,就是 Technical Debt。
Simplicity (KISS) — 简单的方案几乎总是更对
瑞士军刀什么都能干,但都干得不好。一把好刀,比军刀里所有的兄弟都切得好。
工程师怕漏掉需求,就把系统塞满将来可能用得上的一切。架构师知道:每一项没人用的能力,都是开发上的一笔长期税。复杂度要有意识地引入,有理由才引入;默认选——简单、容易改的方案。这条原则常被称为 KISS —— Keep It Simple, Stupid。
- 是什么
- 默认选能解决问题的最简单方案。
- 为什么
- 简单的容易理解、测试和修改。复杂的,只能忍着。
- 例子
一天一百个后台任务?一张数据库表加 cron 就够了——不需要单独的消息队列(message broker)。
- 陷阱
- "以防万一"地建:每多一个功能,就是一笔长期税。
- 关联
- YAGNI 的姐妹;让 Evolvability 更便宜。
Reversibility — 难以撤销的决策值得更多关注
在餐厅,点错的菜可以退。婚礼上说出的"我愿意",退不了。
架构决策并不都一样——出错的代价不同。选编程语言或数据库,几乎无法撤销。选日志库,一天就能换掉。架构师把不成比例的时间花在不可逆的决策上,可逆的则快速推进。业内常把它们叫作单向门(one-way door)和双向门(two-way door)。
- 是什么
- 一个决策如果错了,撤销它要花多大代价。
- 为什么
- 思考的时间,应该和犯错的代价成正比。
- 例子
- 日志库——一天就能换。主数据库——要迁移好几年。
- 陷阱
- 为可逆的事争论几个星期,却用五分钟定下不可逆的事。
- 关联
不可逆的决策值得写一份 ADR;Interface 和在 External Dependencies 之上自建一层,能让事情更可逆。
Technical Debt — 是有意识的选择,不是偷懒
企业贷款没问题,只要你知道什么时候、怎么还。没有还款计划的贷款,是通往破产的路。
有时此刻正确的做法,是先交付一个以后得重写的东西。这就是 technical debt(技术债)。这很正常。不正常的是不承认它是债。 架构师会写下:做了哪些取舍、为什么、什么时候必须回头处理。
- 是什么
- 现在走的捷径,以后要用重写来偿还。
- 为什么
- 有时今天的速度比整洁更重要——这是一笔正当的交换。
- 例子
- 上线前,币种被写死成了一种。记录下来:"出现第二个市场时回头处理。"
- 陷阱
- 没有记录的债:没人记得它存在,利息却在涨。
- 关联
- 记录在 ADR 里的一次 Trade-off;通过 Migration 偿还。
如何把一个大问题拆成一个个组件,每个都能单独理解、修改和替换。
Decomposition — 架构师的核心技能
一头鲸鱼没法整个吃下去。切成能放进嘴里的小块,就可以了。
大系统是无法理解的。Decomposition(分解)就是把它切成几部分的技能。好的架构,是一组小部件,每个都能单独看懂。如果解释一个组件还得牵出另外三个——这个分解就是坏的。
- 是什么
- 把系统切成若干部分,每一部分都能单独理解。
- 为什么
- 人脑一次只能装下这么多。小的部分才能被理解和修改。
- 例子
- 一家网店:订单、支付、商品目录、账户——每一部分都有自己的职责。
- 陷阱
- 按技术来切("所有 controller 放这里"),而不是按含义来切。
- 关联
切得好不好,用 Cohesion & Coupling 来衡量;各部分通过 Interface 对话。
Cohesion & Coupling — 内部高内聚,外部低耦合
一套好公寓:厨房围绕一个目的组织(灶台挨着水槽),和邻居只共用一面墙。
一个模块内部,所有东西都应该围绕一件事——这是高内聚。模块之间——通过清晰的接口、接触降到最少——这是低耦合。如果你改了一个模块,却得去动另外五个——它们之间的耦合就太高了。
- 是什么
Cohesion(内聚)——模块的各个部分有多专注于一件事。Coupling(耦合)——模块之间有多依赖彼此。
- 为什么
- 这样一个改动就留在一个模块里,而不是像波浪一样扩散。
- 例子
- 税的计算放在"账单"里,而不是散落在订单、邮件和报表里。
- 陷阱
- "改一个,动五个":高耦合的明显信号。
- 关联
- Decomposition 的衡量尺;靠 Interface 维系。
Layers & the Dependency Rule — 分层讲的是依赖的方向
房子的楼层:三楼立在二楼上,二楼立在一楼上,一楼立在地基上。你不能把地基放到三楼上。
在好的架构里,业务逻辑不知道数据库、网页界面或框架的存在。内层不依赖外层——这就是依赖规则(dependency rule)。需要换数据库或框架时,业务逻辑原封不动。这就是一句话讲完的 Clean Architecture。
- 是什么
- 依赖指向内部:细节认识核心,核心不认识细节。
- 为什么
- 换数据库或框架,不用动业务规则。
- 例子
- "买三件打九折"这条规则既不引入 SQL 也不引入 HTTP——不用数据库就能测试。
- 陷阱
- SQL 查询和 HTTP 响应直接写在业务逻辑里。
- 关联
- 通过 Interface 起作用;在架构模式课程(Hexagonal、Clean)里有深入讲解。
Interface over Implementation — 接口比实现更重要
你不在乎大楼里电梯是什么牌子。你在乎的是按钮的用法始终一样、门开得可靠、声音安静。
一个模块和另一个模块对话时,重要的是它们之间的契约(接口),而不是对方内部怎么造的(实现)。先设计边界(API、类的接口、消息格式),再做实现。实现可以随便换;契约只有在完全清楚代价时才能改。
- 是什么
- 先设计各部分之间的边界,再做内部。
- 为什么
- 只要契约不变,实现就可以自由更换。
- 例子
一个接口
sendEmail(to, text):今天背后是一家邮件服务商,明天换另一家——调用它的代码不用变。- 陷阱
- 调用方绕过接口,直接伸手去碰另一个模块的内部。
- 关联
- 在系统边缘,接口就成了 API Contract;是 Dependency Rule 的基础。
Monolith vs Microservices — 是成熟度的问题,不是潮流
一个厨师在小厨房里做一道菜,比十个厨师在 Slack 上商量着做还快。每小时一百道菜,十个厨师就做得更好。
单体(monolith)是整体部署的一个应用。微服务(microservices)是许多小服务,各自独立部署。微服务解决的是组织规模和负载的问题,不是代码质量的问题。小团队上微服务,是没人要的复杂度。架构师从单体起步,真的出现痛点时再拆,而不是"因为流行"。
- 是什么
- 一个整体的应用——还是许多独立的服务。
- 为什么
- 微服务给大团队、大负载提供独立部署和独立扩展。
- 例子
- 五个人的团队做产品——单体。二十个团队在同一次部署里互相踩脚——该拆了。
- 陷阱
- 为了赶潮流上微服务:网络、故障和一致性的麻烦全来了,却没有回报。
- 关联
沿着 Decomposition 来拆;拆完后服务之间需要 Sync vs Async 和 Idempotency。
系统里最持久的部分。代码模式会被重写;数据模型能活好多年。
Data Model — 它比其他一切都活得久
房子的地基,比一个世纪里盖在它上面的所有墙和屋顶都活得久。
语言会过时,框架会更替,界面会被重画十遍。一开始定下的数据模型会留下来——因为在运行中的系统上改它,代价极高。它是架构师最先花时间的东西,也是最后才急着去改的东西。
- 是什么
- 系统里有哪些实体,它们的字段和关系。
- 为什么
- 它是活得最久的部分——事后最贵的改动。
- 例子
一开始就要定:一个订单是一个收货地址还是多个?以后再改,就得迁移上百万行数据。
- 陷阱
- 一个晚上画好几张表,然后在上面建好几年。
- 关联
- 每个事实放在哪——Single Source of Truth;怎么改它——Migration。
Single Source of Truth — 每个事实只住在一个地方
如果家里每个人都有自己的购物清单,会有两个人去买牛奶,没人去买面包。
同一份信息放在两个地方,必然走向不一致。**single source of truth(SSOT,单一事实来源)**原则:架构师要确定系统里每个事实住在哪里。如果数据被复制了(比如为了性能),就必须明确:哪个是事实来源,哪个是缓存或投影。
- 是什么
- 每个事实有一个主要的家;其余都是拷贝。
- 为什么
- 拷贝出现分歧时,清楚该信哪一个。
- 例子
- 商品的价格住在商品目录里。搜索索引和网站缓存是拷贝,由商品目录重建。
- 陷阱
- 两个系统都以为自己拥有同一个字段。
- 关联
- 拷贝就是 Cache 和投影;拷贝越少——State 越少。
State — 是复杂度;越少越好
干净的桌面让工作更轻松。杂乱的桌面逼你记住东西在哪,还不断让你分心。
存在某处的每一份状态,都是可能失去同步、丢失、或与其他状态矛盾的东西。好的架构追求无记忆的函数(stateless,无状态)和生命周期短的变量。状态只留在真正需要它的地方——通常是数据库。
- 是什么
- 系统在两次请求之间记住的一切。
- 为什么
- 状态越少——越少的地方可能出现分歧或丢失。
- 例子
- 购物车放在数据库里,而不是服务器内存里:服务器重启——购物车还在。
- 陷阱
- 状态放在某一台服务器的内存里:服务器挂了——数据就没了。
- 关联
- 无状态的服务很容易复制多份——这就是 Scalability。
Transactions — 意图的原子性
转账是两个动作,但只有一个意图。要么两次写入都发生了,要么都没发生。只做一半就是灾难。
当几个改动必须作为一个整体发生时,就需要事务(transaction)。这种性质叫原子性(atomicity)。架构师知道系统里哪些操作是原子的、哪些可能只失败一半,以及如何与之共处。在分布式系统里,这成了核心问题(two-phase commit、saga、idempotency)。
- 是什么
- 几个改动,要么全部发生,要么都不发生。
- 为什么
- 否则系统会卡在"做了一半"的状态里。
- 例子
- 转账:从 A 扣款、给 B 入账——放在一个事务里。
- 陷阱
- 以为一个事务能跨多个服务。那种情况下需要 saga 和 idempotency。
- 关联
- 跨服务时要靠 Idempotency;见架构模式课程里的 Saga 模式。
Cache — 第二份“真相”,而且它会说谎
冰箱上的日历比看手机快。但如果有人在手机上改了日程——冰箱上的那份就已经在说谎了。
缓存(cache)带来速度,却拿走了新鲜度的保证。架构师始终要清楚:缓存了什么、缓存多久、缓存说谎了会怎样。缓存最难的问题不是怎么加——而是怎么让它失效(cache invalidation)。
- 是什么
- 数据的一份快速拷贝,放在读取者近旁。
- 为什么
- 速度,以及减轻数据源的压力。
- 例子
- 数据库里价格变了——缓存里还是旧的。顾客看到一个价格,付的却是另一个。
- 陷阱
- 加了缓存,却没想好什么时候、怎么清掉它。
- 关联
- 是 Single Source of Truth 之下的一份拷贝,不是第二份真相。
最大的问题不出在组件内部,而出在组件之间。
API Contract — 系统的边界是契约,不是代码
国与国的边界不是一道栅栏——而是一套规则:什么可以带过去、谁可以入境、需要什么护照。
API、消息格式、文件格式——这些是对所有使用者的承诺。改契约,就是改承诺,代价很高。好的架构师设计契约时,会让它能扩展而不破坏现有的客户端(加字段,绝不删字段)。这种性质叫向后兼容(backward compatibility)。
- 是什么
- 一份公开的承诺:接受哪些请求,返回什么。
- 为什么
- 有你控制不了的客户端依赖它。
- 例子
响应里加了个
currency字段——老应用直接忽略。把price改了名——老应用就崩了。- 陷阱
- 像改内部代码一样随便地改契约。
- 关联
- Interface over Implementation 的对外面孔;通过 Migration 来改。
Sync vs Async — 在时间上选择怎样耦合
打电话:双方必须同时在线。写信:收信人方便的时候再读。
同步(synchronous)调用把两个服务在时间上绑在一起:接收方挂了,发送方也卡住。通过队列的异步(asynchronous)消息打破了这种绑定:发送方把信放下就继续往前走。这带来韧性,但也增加复杂度——延迟、重复、顺序问题。
- 是什么
- 马上等答复(打电话)——或者发出去就不等了(写信)。
- 为什么
- 异步让服务解耦:一个出故障,不会拖停另一个。
- 例子
- 下订单——同步(顾客在等)。收据邮件——异步,通过队列。
- 陷阱
- 忘了异步的代价:延迟、重复、消息乱序。
- 关联
- 重复要求 Idempotency;在微服务之间很常见。
Idempotency — 一个操作可以放心地重复执行
有人叫电梯去三楼。就算十个人再按一遍同一个按钮,电梯还是去三楼,什么也不会坏。
在真实的系统里,调用会被重试——因为超时、网络故障、重启。幂等(idempotent)的操作,重复执行结果不变。一个重试起来很危险的操作,是一颗定时炸弹。 架构师默认把关键操作(支付、消息处理)做成幂等的。
- 是什么
- 重复执行一个操作,结果和只执行一次一样。
- 为什么
- 重试不可避免:超时、网络故障、重启。
- 例子
客户端带着唯一的键发起支付。同一个键又来了一次——服务器返回之前的结果,而不是再扣一次款。
- 陷阱
- "我们从来不会遇到重试"——会的,只是你还没看到。
- 关联
- Async 和重试时必不可少;是 Reliability 的基础之一。
External Dependencies — 永远是风险
如果晚饭靠街对面的披萨店——没什么大不了,还有备选。如果靠全城唯一的肉类供应商——就是要命的事。
每一个外部依赖——API、服务、库——都可能宕机、变慢,或者改了它的契约。架构师要决定出现这些情况时会怎样:整个系统倒下,还是降级继续运行?通常值得把外部依赖隔离在你自己的一层(adapter,适配器)后面——这样以后才能替换。
- 是什么
- 一切你控制不了的东西:别人的 API、服务、库。
- 为什么
- 它们出故障或变更时,你的系统不该跟着倒下。
- 例子
支付服务商藏在你自己的
PaymentGateway接口后面。换服务商,意味着写一个新 adapter,而不是重写订单。- 陷阱
- 对服务商 SDK 的调用散落在整个代码里。
- 关联
Interface over Implementation 的一种应用;Reliability(fallback,备用方案)的一部分。
不是"系统做什么",而是"做得有多好"。它们叫非功能性需求(NFR)或质量属性——客户永远不会明说,却一定会要求的东西。
Security — 不是一个功能,而是系统的生活方式
在博物馆里,安全不是门口的一个摄像头——而是层层设防:闸机、保安、每个展厅的摄像头、展柜上的锁。
安全(security)没法在最后才补上。它要建在每一层:认证、授权、加密、隔离、审计、最小权限。这种分层的做法叫 defense in depth(纵深防御)。架构师从第一天就考虑它,在每个决策上都问:"如果攻击者从这里进来了呢?"
- 是什么
- 系统每一层都有防护,而不是门口只有一把锁。
- 为什么
- 迟早有一层会被攻破——其余的必须顶住。
- 例子
- 报表服务对数据库只有只读权限——就算它被黑了,数据也删不掉。
- 陷阱
- "发布之前再加安全。"
- 关联
- 审计依赖 Observability;隔离依赖清晰的边界。
载三个人的出租车,不该像高铁那样跑。高铁也开不进小巷。
"让它快一点"是毫无意义的需求。架构师用数字来表述性能(performance):每秒多少请求(throughput,吞吐量),能容忍多大延迟(latency),高峰时系统表现如何,超过上限会发生什么。过早优化是浪费;完全不考虑则是灾难。
- 是什么
- 系统能处理多少、响应多快——用数字表示。
- 为什么
- 没有数字,就没法判断够不够,也不知道在哪里停手。
- 例子
- "每秒 200 个请求时,搜索在 300 毫秒内响应;高峰时——排队,而不是崩溃。"
- 陷阱
- 只说"让它快一点"却没有数字——还去优化并不疼的地方。
- 关联
- 负载增长——Scalability;通过 Observability 来度量。
Scalability — 不重写也能增长的能力
设计时就留了加盖二楼余地的房子,可以不拆就扩建。没留这个余地的房子——只能推倒重建。
Scalability(可扩展性):系统必须朝两个方向增长——更多的用户(横向——加服务器)和更多的功能(模块化——加模块而不破坏旧的)。这要在设计时就埋进去,而不是事后补。
- 是什么
- 负载和功能都能增长,而不用重写的能力。
- 为什么
- 成功的系统会增长——边跑边重写,代价很高。
- 例子
- 负载均衡器后面的无状态服务:负载涨了——再加三个副本。
- 陷阱
- 只有一百个用户,却按一百万用户来建。
- 关联
- 建立在 State(无状态)和 Cohesion & Coupling(模块)之上。
Reliability — 系统出故障时的表现
好船不是不会沉的船(那样的船不存在)——而是有十个隔舱的船,一个被撞破了,其余的仍然浮着。
系统的每个部分终究都会坏。Reliability(可靠性)不是"永远不坏"——而是"坏了也能扛住"。架构师不试图阻止故障——而是设计系统怎样扛过故障:故障隔离、重试、优雅降级、fallback。首要规则:一个组件的故障,不能搞垮整个系统。
- 是什么
- 系统某一部分坏了之后,整体怎么表现。
- 为什么
- 一切都会坏——唯一的问题是一处故障会不会拖垮其他部分。
- 例子
- 推荐服务挂了——商店显示"热门商品",而不是个性化推荐,但照常卖货。
- 陷阱
- 仿佛故障不会发生一样地去建系统。
- 关联
- 重试需要 Idempotency;Observability 让你看见故障。
Observability — 看不见的东西,你修不了
一辆车,没有油量表、没有温度计、没有机油压力指示灯。一路开到彻底趴窝。
Observability(可观测性):系统必须讲出自己的状态——日志(logs)、指标(metrics)、链路追踪(traces)。凌晨三点出了问题,你只有几分钟去找原因。如果一开始没有设计可观测性,事后再加就晚了——你连该看哪里都不知道。
- 是什么
- 根据系统发出的信号,弄清系统内部正在发生什么的能力。
- 为什么
- 没有信号,查故障只能盲目地找上好几个小时。
- 例子
指标显示凌晨 03:00 错误激增,追踪显示某个外部 API 很慢,日志显示具体是什么错误。
- 陷阱
- 等第一次大故障之后才去加日志。
- 关联
- Reliability 和 Performance 的眼睛;Security 里审计的基础。
系统会活很久。它一定会被改。好的架构,就是容易改的架构。
Evolvability — 为变化做好准备,比今天正确更重要
在一座新城里,马路修得比今天需要的更宽。十年后车多了,窄路就得拆掉重修。
架构师不试图预测未来的每一个功能(做不到)。他们留出余地,让新功能能加进来而不破坏现有的东西。这种性质叫 evolvability(可演进性)。实践中意味着:边界清晰、契约可扩展、把"做什么"和"怎么做"分开。
- 是什么
- 在系统里增加或修改东西有多便宜。
- 为什么
- 需求一定会变——只是不知道会怎么变。
- 例子
- 新增一种支付方式,是加一个新的 adapter,而不是在二十个地方改代码。
- 陷阱
- 试图猜出未来的每个功能,并提前建好。
- 关联
来自 Interface、Cohesion & Coupling,以及可扩展的 API Contract。
从旧房子搬到新房子,是个持续几个月的过程。你不能把东西全扔了再全买一遍。
当系统需要改变时(新数据库、新协议、新数据模型)——这不是"从头重写",而是"带着系统走完一段过渡"。这就是 migration(迁移)。真实的系统在变更期间一直在运行。架构师从一开始就考虑迁移的路径,否则过渡就成了不可能的事。
- 是什么
- 把一个正在运行的系统,一步一步从旧搬到新。
- 为什么
- 真实的系统不能为了翻修而关掉。
- 例子
- 换新数据库:先双写,再改成从新库读,最后关掉旧库。
- 陷阱
- "一个季度就能从头重写"——结果同时养着两套系统两年。
- 关联
- Data Model 和 API Contract 怎么改;Technical Debt 怎么还。
YAGNI & Deletion — 删除比添加更重要
衣柜不能无限扩大。有时必须扔掉旧的,才能给新的腾地方。
任何成功的系统都会积累死代码、没人用的功能、过时的集成。这是每个开发者每天都在交的税。 架构师会定期问:"我们能删掉什么?"——有时这就是一年里最有用的架构决策。
最结实的墙,是从来不用拆的墙,因为后面没有被塞进任何多余的东西。
有时正确的架构决策,是不做架构决策。不要"为了将来"引入一层抽象。不要"以后某天会用到"就拆分单体。不要"以防万一"就建一套插件系统。这条原则叫 YAGNI —— You Aren't Gonna Need It(你不会需要它)。好的架构师知道什么时候该说"先不做"或"我们不需要这个"。
- 是什么
- 现在不需要的就不要建,不再需要的就删掉。
- 为什么
- 最好的架构是不需要的那一个:成本为零。
- 例子
删掉了一个只有两个客户在用的集成:少了一千行代码,少了一个依赖,少了深夜的告警。
- 陷阱
- "以防万一"的抽象和插件。
- 关联
- Simplicity 的延伸;为 Evolvability 腾出空间。
没人理解的架构等于不存在。一半的工作,是把它传达出去。
ADR — 架构决策要写下来,否则就丢了
口头的遗嘱,随立遗嘱的人一起消失。写下来的,能传好几代人。
每一个重要的架构决策都需要记录:选了什么、有哪些备选、为什么否掉、接受了哪些妥协。这份文档现在用不上——它是一年后用的,那时来了新人,问"为什么这里做得这么怪?"标准格式是 ADR(Architecture Decision Record,架构决策记录)。
- 是什么
- 对一个架构决策及其理由的简短记录。
- 为什么
- 一年后没人记得为什么。记录记得。
- 例子
"ADR-007:用 Postgres,不用 MongoDB。我们需要订单与支付之间的事务。代价——更严格的表结构。如果出现非结构化数据,再重新审视。"
- 陷阱
- 只写"是什么",不写"为什么",也不写被否掉的选项。
- 关联
- 里面装着 Trade-offs、Technical Debt 和不可逆的决策。
C4 Model — 一张图胜过千言万语
用文字描述怎么走到一栋房子,又长又常常没用。给人看一张地图,一秒钟就够。
架构师手边总有一张系统的图:有哪些组件、怎么连接、边界在哪里。不求完美——能用就行。它用来向新人快速解释,也让你能看清系统的全貌。**C4 模型(Context、Container、Component、Code)**简单,对大多数情况已经够用。
- 是什么
- 四个层级的图——从整个系统一直到代码。
- 为什么
- 每类读者各看各的缩放层级,而不是一张图包打天下。
- 例子
- Context:商店、顾客、支付服务商。Container:网页应用、API、数据库、队列。
- 陷阱
- 一张巨大的图,所有东西都画在同一层,什么也看不清。
- 关联
- 把 Decomposition 及其边界画出来;与 ADR 互补。
好医生用病人听得懂的话解释诊断。坏医生满口医学术语,病人点点头,什么也没听懂。
如果架构师只会向其他工程师解释决策,他们就丢掉了一半的工作。业务、产品、法务、安全——所有的干系人(stakeholders)都需要理解后果。把技术决策翻译成业务语言(成本、风险、时间),是架构师的核心技能之一。
对什么请求都答应的外科医生,会失去病人。好的外科医生会在手术会致命时拒绝。
团队常常提出听起来合理、却会造成长期问题的要求。架构师必须敢于反驳、解释风险,有时还要守住底线——即使不受欢迎。同时也要——敢于承认自己错了,当反方的论据更有力时,改变决定。
- 是什么
- 用听者的语言解释决策:成本、风险、时间。
- 为什么
- 没人理解的决策,不会得到支持——或者会被推翻。
- 例子
不说"我们需要消息队列",而说"没有它,邮件服务一出故障,我们就会丢订单——每月大约这么多。"
- 陷阱
- 满嘴术语——或者为了避免争吵什么都答应。
- 关联
- 建立在 Trade-offs、ADR 和 C4 Model 之上。
架构师在任何决策之前、以及一天结束时都要过一遍的检查清单。还有本课所有概念,汇在一页纸上。
- 究竟在解决什么问题? 不是被提出来的任务,而是它背后的问题。
- 有哪些备选? 至少三个,包括"什么都不做"。
- 这个选择让我失去了什么? 至少说出两个缺点。
- 这个决策有多可逆? 可逆的——快速定。不可逆的——多想想。
- 两三年后这会是什么样子? 不是理想的样子——是现实的样子。
- 除了我,还有谁需要理解这个决策? 以及我该怎么向他们解释。
- 这个系统里最脆弱的是什么? 负载一上来,什么最先坏?
- 哪些改动容易,哪些痛苦? 这就是衡量架构质量的尺子。
- 每个重要事实的真相放在哪里? 它的拷贝又在哪里——缓存、副本、投影。
- 组件之间哪些边界是明确的,哪些是偶然形成的? 偶然形成的就是技术债。
- 每个外部依赖坏了会怎样? 一个一个地过。
- 如果今天从头开始,我会做哪些不同的事? 那就是演进的方向。
- 你花在讨论和设计上的时间,比写代码更多。
- 你常在问"怎么做"之前先问**"为什么"**。
- 你把取舍大声说出来,而不是试图藏起来。
- 你写文档和图比大多数人都勤。
- 你看到整个系统,并能把任何一部分放在整体里讲清楚。
- 你会想到一年后要在这上面工作的人——并把东西写得让他们用着舒服。
- 你偶尔会因为用不上,而拒绝一项漂亮的技术。
这些概念如何相连
一个工具箱:没有钉子,锤子没用;没有锤子,钉子也没用。
本课的概念不是孤立存在的。一个决策从 Problem Framing 开始,当作 Trade-off 来权衡,检查它的 Reversibility,再写进 ADR。系统用 Decomposition 来切,切得好不好用 Cohesion & Coupling 来评判,各部分在 Interface 处相遇。数据的拷贝住在 Single Source of Truth 之下,异步世界里的重试则要求 Idempotency。
速查表:全部 31 个概念
心态
- Problem Framing找到真正的问题
先问"为什么",再问"怎么做"。找到请求背后的问题。
- Long-term Thinking为后果负责
从三年后看一个决策;为后果负责,而不是为代码负责。
Trade-offs 与决策
- Trade-offs取舍
每个选择都给你一些东西,也拿走一些东西。把轴线大声说出来。
- SimplicityKISS
默认简单。没人用的功能是一笔长期税。
- Reversibility单向门 / 双向门
可逆的快速定,不可逆的慢慢来。
- Technical Debt技术债
现在走捷径,以后还账。可以——只要写下来。
系统设计
- Decomposition拆分成部分
每个部分都能单独理解。
- Cohesion & Coupling内部专注,外部少连
模块内部——只做一件事;模块之间——连接最少。
- Dependency Rule分层, Clean Architecture
依赖指向内部:业务逻辑对数据库和框架一无所知。
- Interface over Implementation契约优先
先定边界,再做实现。实现可以随便换。
- Monolith vs Microservices一个应用还是多个服务
从单体开始,真的疼了再拆。
数据与状态
- Data Model实体、字段、关系
活得比代码久。先想它。
- Single Source of TruthSSOT
每个事实有一个主要的家;其余都是拷贝。
- Statestateless vs stateful
状态就是复杂度。保持少量,并放在一个地方。
- Transaction原子性
几个改动——要么全做,要么全不做。
- Cache缓存失效
一份会说谎的快速拷贝。难的是什么时候清掉它。
连接与边界
- API Contract向后兼容
对客户端的承诺。只扩展,不收回。
- Sync vs Async打电话还是写信
同步在时间上耦合;异步更有韧性,但带来延迟和重复。
- Idempotency可安全重复
重复一次,结果相同。支付和消息必备。
- External Dependencies自己的 adapter
别人的系统会出故障、会变更。把它们藏在你自己的一层后面,规划好降级。
非功能性需求
- Securitydefense in depth
从第一天起就分层防护,最小权限。
- Performancelatency, throughput
不是"快",而是数字:负载、延迟、高峰时的表现。
- Scalability横向与模块化
靠服务器和模块来增长,不用重写。
- Reliability容错
一个部分出故障,不能搞垮整个系统。
- Observability日志、指标、追踪
系统会讲出自己的情况。从一开始就设计进去。
演进
- Evolvability为变化做好准备
给新东西留出余地,而不是提前去猜它。
- Migration过渡期
不是"从头重写",而是带着系统走完过渡。
- YAGNI & DeletionYou Aren't Gonna Need It
不要多造;不再需要的就删掉。
沟通
- ADRArchitecture Decision Record
选了什么、有哪些备选、为什么、代价是什么。
- C4 ModelContext · Container · Component · Code
四个缩放层级的系统图。
- Stakeholder Communication
用成本、风险和时间来说话;该反驳时反驳,错了就承认。
架构师不是知道得更多的人。而是在开始之前想得更久的人。