fedorthinks

速成课 · No. 02

不是一个头衔,不是一个职级,也不是一场考试的结果。而是一种看问题的眼光——今天的决策,塑造未来三年。

31 个概念 · 几乎每个都配一张图 · 结尾有速查表

§ 01

架构师和工程师的区别在哪。不在于知识——而在于看问题的方式。

课程地图:八组共 31 个概念

一张地铁线路图:先看到全部线路,再一站一站地坐过去。

架构不是一堆技术——而是一组动手写代码之前要问的问题。每个问题在业内都有自己的名字:Trade-offs、Coupling、Idempotency、ADR。本课把它们归成八组——第 1–8 章依次讲这八组。

1 · MindsetProblem framingLong-term thinking2 · Trade-offsTrade-offsSimplicity (KISS)ReversibilityTechnical debt3 · System designDecompositionCohesion / couplingDependency ruleInterface firstMonolith vs micro4 · Data & stateData modelSource of truthStateTransactionsCache5 · BoundariesAPI contractSync vs asyncIdempotencyExternal deps6 · Quality (NFRs)SecurityPerformanceScalabilityReliabilityObservability7 · EvolutionEvolvabilityMigrationYAGNI / deletion8 · CommunicationADRC4 modelStakeholders
八组概念——第 1–8 章依次讲这八组。

怎么读这门课

护照:每本的栏目都一样,所以容易比较。

每个概念都按同一套方式讲:它的规范名称(记住它——这是你要去搜索的词,也是面试里会被问到的词)、一个生活中的画面、一张图,以及一张带栏目的卡片:是什么、为什么、例子、陷阱(人们通常在哪里出错)和关联概念。每章以它的关键问题收尾;全课以检查清单和速查表收尾。

Problem Framing — 质疑任务,而不只是质疑方案

病人进门就要一张处方。好医生先问哪里疼——也许会发现,他点名要的那种药本来就开错了。

当有人说"做 X"时,工程师问"我该怎么做 X"。架构师问的是**"我们为什么需要 X,有没有别的东西能解决真正的问题"**。这就是 problem framing(界定问题)。任务常常本身就提错了——而某人点单要来的那个方案,一年后变成了问题本身。

“Build X”request as posedWhy?Real problembehind the requestOption Xwhat was askedOption Ya simpler fixDo nothingalso an optionengineer: “how do I build X?” · architect: “why X at all?”
先问“为什么?”,再找真正的问题,然后才是选项——包括“什么都不做”。
PracticeProblem Framing· 找到真正的问题
是什么
在动手做方案之前,先找出请求背后真正的问题。
为什么
最贵的错误,是把错误的东西做得完美。
例子

有人让你"加一个 Excel 导出"。你问为什么——原来是大家觉得系统里的报表不好用。那就去改报表。

陷阱
不问"为什么",直接跳到"怎么做"。
关联
为 Trade-offs 提供选项;"什么都不做"往往就是 YAGNI 的答案。

Long-term Thinking — 以年为单位的视野,为后果负责

工程师为周六要搬进来的那家人盖房子。架构师则问:"等他们有了第三个孩子呢?等母亲搬来同住呢?等他们想把一楼出租呢?"

工程师解决被提出来的那个问题。架构师解决三年后它会变成的样子那个问题。这意味着:为还没用上的能力做设计,同时刻意舍弃那些听起来优雅、却永远用不上的能力。

木匠把钉子钉歪了,一天就能修好。架构师把承重墙放错了位置,房子修不了——只能将就着住。

所以架构师要为后果负责,而不是为代码负责。工程师的错误,一周就能修好。架构师的错误,公司要为它付好几年的账——付在技术债里、付在流失的团队里、付在错过的机会里。架构师必须在第一行代码写下之前就想清楚,并且有勇气在架构扛不住真实需求时叫停工程。

Engineer’s mistakefixed in a weekArchitect’s mistaketech debt · lost team · missed chancesweekmonth1 year3+ years
代码错误一周就能修好。架构错误要付好几年的账。
ConceptLong-term Thinking· 为后果负责
是什么
用一个决策在两三年后的样子来评判它。
为什么
架构错误最贵:要付好几年的账。
例子
选数据库表结构时,问一句:"客户多了十倍怎么办?再多出一种语言怎么办?"
陷阱
把"想在前面"和"提前全做好"混为一谈。想——要;多造——不要。
关联
由 Simplicity 和 YAGNI 来平衡;帮你判断 Reversibility。

工程师把事做对。架构师做对的事。

§ 02

架构是选择放弃什么的艺术。不存在完美的方案。

Trade-offs — 每个决策都有得有失

自行车比走路快,但下雨会淋湿。汽车淋不着,但会堵在路上。地铁不堵,但不能送到家门口。

工程师选技术时,常常想找"最好的"。这是幻觉。架构师知道:任何选择都是在好几条轴线上的取舍——开发速度对可维护性,简单对灵活,现在的成本对三年后的成本。把轴线说出来、有意识地选择,就是架构工作。

every choice is a point on several axesDev speedMaintainabilitySimplicityFlexibilityCost nowCost in 3 years
一个决策,是多条轴线上的一个点。往一边走——另一边就吃亏。
ConceptTrade-offs· 取舍
是什么
每个选择都给你一些东西,也拿走一些东西。没有"最好"。
为什么
大声说出来的取舍,可以讨论、可以回头再看。藏起来的,不行。
例子
托管的云服务:起步快(得),但量大时更贵、也更难离开(失)。
陷阱
一味寻找"最好的技术",而不是合适的那个。
关联
记录在 ADR 里;今天有意识的损失,就是 Technical Debt。

Simplicity (KISS) — 简单的方案几乎总是更对

瑞士军刀什么都能干,但都干得不好。一把好刀,比军刀里所有的兄弟都切得好。

工程师怕漏掉需求,就把系统塞满将来可能用得上的一切。架构师知道:每一项没人用的能力,都是开发上的一笔长期税。复杂度要有意识地引入,有理由才引入;默认选——简单、容易改的方案。这条原则常被称为 KISS —— Keep It Simple, Stupid。

Everything “just in case”featurefeatureunusedunusedunusedunusedSimple by defaultfeaturefeatureadded when really neededunused = a permanent taxeasy to change
左边:你在为没人用的东西付钱。右边:真需要时再加。
PrincipleSimplicity· KISS, Keep It Simple
是什么
默认选能解决问题的最简单方案。
为什么
简单的容易理解、测试和修改。复杂的,只能忍着。
例子

一天一百个后台任务?一张数据库表加 cron 就够了——不需要单独的消息队列(message broker)。

陷阱
"以防万一"地建:每多一个功能,就是一笔长期税。
关联
YAGNI 的姐妹;让 Evolvability 更便宜。

Reversibility — 难以撤销的决策值得更多关注

在餐厅,点错的菜可以退。婚礼上说出的"我愿意",退不了。

架构决策并不都一样——出错的代价不同。选编程语言或数据库,几乎无法撤销。选日志库,一天就能换掉。架构师把不成比例的时间花在不可逆的决策上,可逆的则快速推进。业内常把它们叫作单向门(one-way door)和双向门(two-way door)。

Two-way dooreasy to undologging librarydecide fastOne-way doorhard to undoprogramming languagedatabasethink slowspend your time where a mistake is expensive
可逆的事快速定。不可逆的——慢慢来,并把“为什么”写下来。
ConceptReversibility· 单向门 / 双向门
是什么
一个决策如果错了,撤销它要花多大代价。
为什么
思考的时间,应该和犯错的代价成正比。
例子
日志库——一天就能换。主数据库——要迁移好几年。
陷阱
为可逆的事争论几个星期,却用五分钟定下不可逆的事。
关联

不可逆的决策值得写一份 ADR;Interface 和在 External Dependencies 之上自建一层,能让事情更可逆。

Technical Debt — 是有意识的选择,不是偷懒

企业贷款没问题,只要你知道什么时候、怎么还。没有还款计划的贷款,是通往破产的路。

有时此刻正确的做法,是先交付一个以后得重写的东西。这就是 technical debt(技术债)。这很正常。不正常的是不承认它是债。 架构师会写下:做了哪些取舍、为什么、什么时候必须回头处理。

Shortcut nowship fasterRecorded debtwhat · why · whenPaid backplanned rewriteHidden debtnobody wrote it downInterest growsslower every monthconsciousno record
记录在案的债,按计划偿还。没记录的债,利滚利——团队每个月都变慢一点。
TermTechnical Debt· 技术债
是什么
现在走的捷径,以后要用重写来偿还。
为什么
有时今天的速度比整洁更重要——这是一笔正当的交换。
例子
上线前,币种被写死成了一种。记录下来:"出现第二个市场时回头处理。"
陷阱
没有记录的债:没人记得它存在,利息却在涨。
关联
记录在 ADR 里的一次 Trade-off;通过 Migration 偿还。
§ 03

如何把一个大问题拆成一个个组件,每个都能单独理解、修改和替换。

Decomposition — 架构师的核心技能

一头鲸鱼没法整个吃下去。切成能放进嘴里的小块,就可以了。

大系统是无法理解的。Decomposition(分解)就是把它切成几部分的技能。好的架构,是一组小部件,每个都能单独看懂。如果解释一个组件还得牵出另外三个——这个分解就是坏的。

Whole systemtoo big to understandsplitOrdersexplainable alonePaymentsexplainable aloneCatalogexplainable aloneAccountsexplainable alone
整个系统没法理解。能自己讲明白自己的各个部分,可以。
PracticeDecomposition· 拆分成部分
是什么
把系统切成若干部分,每一部分都能单独理解。
为什么
人脑一次只能装下这么多。小的部分才能被理解和修改。
例子
一家网店:订单、支付、商品目录、账户——每一部分都有自己的职责。
陷阱
按技术来切("所有 controller 放这里"),而不是按含义来切。
关联

切得好不好,用 Cohesion & Coupling 来衡量;各部分通过 Interface 对话。

Cohesion & Coupling — 内部高内聚,外部低耦合

一套好公寓:厨房围绕一个目的组织(灶台挨着水槽),和邻居只共用一面墙。

一个模块内部,所有东西都应该围绕一件事——这是高内聚。模块之间——通过清晰的接口、接触降到最少——这是低耦合。如果你改了一个模块,却得去动另外五个——它们之间的耦合就太高了。

High cohesion · low couplingOrdersorderline itemstotalsBillinginvoicetaxpaymentone clear interfaceLow cohesion · high couplingModule AordertaxemailModule Binvoicetotalsuserschange one → touch five
左:每个模块只做一件事,只有一条连线。右:一切混在一起,和所有东西都连着。
PrincipleCohesion & Coupling· 内部专注,外部少连
是什么

Cohesion(内聚)——模块的各个部分有多专注于一件事。Coupling(耦合)——模块之间有多依赖彼此。

为什么
这样一个改动就留在一个模块里,而不是像波浪一样扩散。
例子
税的计算放在"账单"里,而不是散落在订单、邮件和报表里。
陷阱
"改一个,动五个":高耦合的明显信号。
关联
Decomposition 的衡量尺;靠 Interface 维系。

Layers & the Dependency Rule — 分层讲的是依赖的方向

房子的楼层:三楼立在二楼上,二楼立在一楼上,一楼立在地基上。你不能把地基放到三楼上。

在好的架构里,业务逻辑不知道数据库、网页界面或框架的存在。内层不依赖外层——这就是依赖规则(dependency rule)。需要换数据库或框架时,业务逻辑原封不动。这就是一句话讲完的 Clean Architecture。

outer layers = detailsdependencies point inward✗ the core never imports the databaseBusiness logicdomain rulesWeb UIDatabaseFramework
细节(界面、数据库、框架)依赖业务逻辑——绝不反过来。
PrincipleDependency Rule· 分层, Clean Architecture
是什么
依赖指向内部:细节认识核心,核心不认识细节。
为什么
换数据库或框架,不用动业务规则。
例子
"买三件打九折"这条规则既不引入 SQL 也不引入 HTTP——不用数据库就能测试。
陷阱
SQL 查询和 HTTP 响应直接写在业务逻辑里。
关联
通过 Interface 起作用;在架构模式课程(Hexagonal、Clean)里有深入讲解。

Interface over Implementation — 接口比实现更重要

你不在乎大楼里电梯是什么牌子。你在乎的是按钮的用法始终一样、门开得可靠、声音安静。

一个模块和另一个模块对话时,重要的是它们之间的契约(接口),而不是对方内部怎么造的(实现)。先设计边界(API、类的接口、消息格式),再做实现。实现可以随便换;契约只有在完全清楚代价时才能改。

CallerInterfacethe contractImpl AtodayImpl Btomorrowthe caller sees only this
调用方只看到接口。接口背后的实现可以替换。
PrincipleInterface over Implementation· 契约优先
是什么
先设计各部分之间的边界,再做内部。
为什么
只要契约不变,实现就可以自由更换。
例子

一个接口 sendEmail(to, text):今天背后是一家邮件服务商,明天换另一家——调用它的代码不用变。

陷阱
调用方绕过接口,直接伸手去碰另一个模块的内部。
关联
在系统边缘,接口就成了 API Contract;是 Dependency Rule 的基础。

Monolith vs Microservices — 是成熟度的问题,不是潮流

一个厨师在小厨房里做一道菜,比十个厨师在 Slack 上商量着做还快。每小时一百道菜,十个厨师就做得更好。

单体(monolith)是整体部署的一个应用。微服务(microservices)是许多小服务,各自独立部署。微服务解决的是组织规模和负载的问题,不是代码质量的问题。小团队上微服务,是没人要的复杂度。架构师从单体起步,真的出现痛点时再拆,而不是"因为流行"。

Monolithstart heresplit when it hurtsteam scale · loadOrdersown deployBillingown deploySearchown deployUsersown deploy✗ small team + microservices = complexity nobody asked for
从单体开始。真的疼了再拆:团队或负载变大的时候。
ConceptMonolith vs Microservices· 一个应用还是多个服务
是什么
一个整体的应用——还是许多独立的服务。
为什么
微服务给大团队、大负载提供独立部署和独立扩展。
例子
五个人的团队做产品——单体。二十个团队在同一次部署里互相踩脚——该拆了。
陷阱
为了赶潮流上微服务:网络、故障和一致性的麻烦全来了,却没有回报。
关联

沿着 Decomposition 来拆;拆完后服务之间需要 Sync vs Async 和 Idempotency。

§ 04

系统里最持久的部分。代码模式会被重写;数据模型能活好多年。

Data Model — 它比其他一切都活得久

房子的地基,比一个世纪里盖在它上面的所有墙和屋顶都活得久。

语言会过时,框架会更替,界面会被重画十遍。一开始定下的数据模型会留下来——因为在运行中的系统上改它,代价极高。它是架构师最先花时间的东西,也是最后才急着去改的东西。

UI designFrameworkLanguageData modellaid down at the start, lives for yearsyears →
界面被重画很多次,框架和语言不断更换——数据模型留下来。
ConceptData Model· 实体、字段、关系
是什么
系统里有哪些实体,它们的字段和关系。
为什么
它是活得最久的部分——事后最贵的改动。
例子

一开始就要定:一个订单是一个收货地址还是多个?以后再改,就得迁移上百万行数据。

陷阱
一个晚上画好几张表,然后在上面建好几年。
关联
每个事实放在哪——Single Source of Truth;怎么改它——Migration。

Single Source of Truth — 每个事实只住在一个地方

如果家里每个人都有自己的购物清单,会有两个人去买牛奶,没人去买面包。

同一份信息放在两个地方,必然走向不一致。**single source of truth(SSOT,单一事实来源)**原则:架构师要确定系统里每个事实住在哪里。如果数据被复制了(比如为了性能),就必须明确:哪个是事实来源,哪个是缓存或投影。

WritesSource of truthprimary databaseCachederived copyRead replicaderived copyProjectionderived copycopies are derived; writes go to the truth only
写入只进事实来源;缓存、副本和投影都是派生的拷贝。
PrincipleSingle Source of Truth· SSOT
是什么
每个事实有一个主要的家;其余都是拷贝。
为什么
拷贝出现分歧时,清楚该信哪一个。
例子
商品的价格住在商品目录里。搜索索引和网站缓存是拷贝,由商品目录重建。
陷阱
两个系统都以为自己拥有同一个字段。
关联
拷贝就是 Cache 和投影;拷贝越少——State 越少。

State — 是复杂度;越少越好

干净的桌面让工作更轻松。杂乱的桌面逼你记住东西在哪,还不断让你分心。

存在某处的每一份状态,都是可能失去同步、丢失、或与其他状态矛盾的东西。好的架构追求无记忆的函数(stateless,无状态)和生命周期短的变量。状态只留在真正需要它的地方——通常是数据库。

App instancestatelessApp instancestatelessApp instancestatelessDatabasestate lives here
应用实例什么都不记——所有状态都在一个地方,数据库。
ConceptState· stateless vs stateful
是什么
系统在两次请求之间记住的一切。
为什么
状态越少——越少的地方可能出现分歧或丢失。
例子
购物车放在数据库里,而不是服务器内存里:服务器重启——购物车还在。
陷阱
状态放在某一台服务器的内存里:服务器挂了——数据就没了。
关联
无状态的服务很容易复制多份——这就是 Scalability。

Transactions — 意图的原子性

转账是两个动作,但只有一个意图。要么两次写入都发生了,要么都没发生。只做一半就是灾难。

当几个改动必须作为一个整体发生时,就需要事务(transaction)。这种性质叫原子性(atomicity)。架构师知道系统里哪些操作是原子的、哪些可能只失败一半,以及如何与之共处。在分布式系统里,这成了核心问题(two-phase commit、saga、idempotency)。

one intent = one transactionDebit A−100Credit B+100Both writtencommitNeither writtenrollbackOnly one✗ never allowed
两次写入都成功,或者都不成功。没有“一半”。
ConceptTransaction· 原子性
是什么
几个改动,要么全部发生,要么都不发生。
为什么
否则系统会卡在"做了一半"的状态里。
例子
转账:从 A 扣款、给 B 入账——放在一个事务里。
陷阱
以为一个事务能跨多个服务。那种情况下需要 saga 和 idempotency。
关联
跨服务时要靠 Idempotency;见架构模式课程里的 Saga 模式。

Cache — 第二份“真相”,而且它会说谎

冰箱上的日历比看手机快。但如果有人在手机上改了日程——冰箱上的那份就已经在说谎了。

缓存(cache)带来速度,却拿走了新鲜度的保证。架构师始终要清楚:缓存了什么、缓存多久、缓存说谎了会怎样。缓存最难的问题不是怎么加——而是怎么让它失效(cache invalidation)。

AppCachefast · may be staleDatabasesource of truthUpdatereadon missinvalidate!the hard part is not adding a cache — it is invalidating it
缓存回答得快,但数据库一有改动就必须把它清掉——否则它就在说谎。
ConceptCache· 缓存失效
是什么
数据的一份快速拷贝,放在读取者近旁。
为什么
速度,以及减轻数据源的压力。
例子
数据库里价格变了——缓存里还是旧的。顾客看到一个价格,付的却是另一个。
陷阱
加了缓存,却没想好什么时候、怎么清掉它。
关联
是 Single Source of Truth 之下的一份拷贝,不是第二份真相。
§ 05

最大的问题不出在组件内部,而出在组件之间。

API Contract — 系统的边界是契约,不是代码

国与国的边界不是一道栅栏——而是一套规则:什么可以带过去、谁可以入境、需要什么护照。

API、消息格式、文件格式——这些是对所有使用者的承诺。改契约,就是改承诺,代价很高。好的架构师设计契约时,会让它能扩展而不破坏现有的客户端(加字段,绝不删字段)。这种性质叫向后兼容(backward compatibility)。

Client v1Client v2API contracta promiseadd a field✓ old clients fineremove a field✗ breaks v1extend the contract; never take things away
加一个字段——老客户端照常运行。删一个字段——老客户端就坏了。
ConceptAPI Contract· 向后兼容
是什么
一份公开的承诺:接受哪些请求,返回什么。
为什么
有你控制不了的客户端依赖它。
例子

响应里加了个 currency 字段——老应用直接忽略。把 price 改了名——老应用就崩了。

陷阱
像改内部代码一样随便地改契约。
关联
Interface over Implementation 的对外面孔;通过 Migration 来改。

Sync vs Async — 在时间上选择怎样耦合

打电话:双方必须同时在线。写信:收信人方便的时候再读。

同步(synchronous)调用把两个服务在时间上绑在一起:接收方挂了,发送方也卡住。通过队列的异步(asynchronous)消息打破了这种绑定:发送方把信放下就继续往前走。这带来韧性,但也增加复杂度——延迟、重复、顺序问题。

Synchronous: coupled in timeService Awaits…Service BdowncallA is stuck tooAsynchronous: decoupledService Amoves onQueueholds messagesService Breads when readyprice of async: delay · duplicates · ordering
上:A 等 B,跟着一起挂。下:A 把消息放进队列,继续往前走。
ConceptSync vs Async· 打电话还是写信
是什么
马上等答复(打电话)——或者发出去就不等了(写信)。
为什么
异步让服务解耦:一个出故障,不会拖停另一个。
例子
下订单——同步(顾客在等)。收据邮件——异步,通过队列。
陷阱
忘了异步的代价:延迟、重复、消息乱序。
关联
重复要求 Idempotency;在微服务之间很常见。

Idempotency — 一个操作可以放心地重复执行

有人叫电梯去三楼。就算十个人再按一遍同一个按钮,电梯还是去三楼,什么也不会坏。

在真实的系统里,调用会被重试——因为超时、网络故障、重启。幂等(idempotent)的操作,重复执行结果不变。一个重试起来很危险的操作,是一颗定时炸弹。 架构师默认把关键操作(支付、消息处理)做成幂等的。

Clientsame request ×3 (timeouts)Payment APIidempotentCharged once$100, not $300repeating the operation gives the same result
同一个请求来了三次——钱只扣了一次。
ConceptIdempotency· 可安全重复
是什么
重复执行一个操作,结果和只执行一次一样。
为什么
重试不可避免:超时、网络故障、重启。
例子

客户端带着唯一的键发起支付。同一个键又来了一次——服务器返回之前的结果,而不是再扣一次款。

陷阱
"我们从来不会遇到重试"——会的,只是你还没看到。
关联
Async 和重试时必不可少;是 Reliability 的基础之一。

External Dependencies — 永远是风险

如果晚饭靠街对面的披萨店——没什么大不了,还有备选。如果靠全城唯一的肉类供应商——就是要命的事。

每一个外部依赖——API、服务、库——都可能宕机、变慢,或者改了它的契约。架构师要决定出现这些情况时会怎样:整个系统倒下,还是降级继续运行?通常值得把外部依赖隔离在你自己的一层(adapter,适配器)后面——这样以后才能替换。

Your coreYour adapterown layerVendor APIcan be slow / downOther vendorswap laterFallbackdegraded mode
核心只和自己的 adapter 对话。它后面是:供应商、备用供应商,或降级模式。
PracticeExternal Dependencies· 自己的 adapter
是什么
一切你控制不了的东西:别人的 API、服务、库。
为什么
它们出故障或变更时,你的系统不该跟着倒下。
例子

支付服务商藏在你自己的 PaymentGateway 接口后面。换服务商,意味着写一个新 adapter,而不是重写订单。

陷阱
对服务商 SDK 的调用散落在整个代码里。
关联

Interface over Implementation 的一种应用;Reliability(fallback,备用方案)的一部分。

§ 06

不是"系统做什么",而是"做得有多好"。它们叫非功能性需求(NFR)或质量属性——客户永远不会明说,却一定会要求的东西。

Security — 不是一个功能,而是系统的生活方式

在博物馆里,安全不是门口的一个摄像头——而是层层设防:闸机、保安、每个展厅的摄像头、展柜上的锁。

安全(security)没法在最后才补上。它要建在每一层:认证、授权、加密、隔离、审计、最小权限。这种分层的做法叫 defense in depth(纵深防御)。架构师从第一天就考虑它,在每个决策上都问:"如果攻击者从这里进来了呢?"

Authentication — who are you?Authorization — what may you do?EncryptionIsolationDataleast privilegeAudit — every access is logged
分层防御:突破了一层,攻击者还会撞上下一层。
QualitySecurity· defense in depth, least privilege
是什么
系统每一层都有防护,而不是门口只有一把锁。
为什么
迟早有一层会被攻破——其余的必须顶住。
例子
报表服务对数据库只有只读权限——就算它被黑了,数据也删不掉。
陷阱
"发布之前再加安全。"
关联
审计依赖 Observability;隔离依赖清晰的边界。

载三个人的出租车,不该像高铁那样跑。高铁也开不进小巷。

"让它快一点"是毫无意义的需求。架构师用数字来表述性能(performance):每秒多少请求(throughput,吞吐量),能容忍多大延迟(latency),高峰时系统表现如何,超过上限会发生什么。过早优化是浪费;完全不考虑则是灾难。

latencyload (req/s)tolerable latencyexpected peakpast the limit:then what?
需求就是数字:能容忍多大的延迟,在多大的负载下。以及超过上限会怎样。
QualityPerformance· latency, throughput
是什么
系统能处理多少、响应多快——用数字表示。
为什么
没有数字,就没法判断够不够,也不知道在哪里停手。
例子
"每秒 200 个请求时,搜索在 300 毫秒内响应;高峰时——排队,而不是崩溃。"
陷阱
只说"让它快一点"却没有数字——还去优化并不疼的地方。
关联
负载增长——Scalability;通过 Observability 来度量。

Scalability — 不重写也能增长的能力

设计时就留了加盖二楼余地的房子,可以不拆就扩建。没留这个余地的房子——只能推倒重建。

Scalability(可扩展性):系统必须朝两个方向增长——更多的用户(横向——加服务器)和更多的功能(模块化——加模块而不破坏旧的)。这要在设计时就埋进去,而不是事后补。

Systemmore users → add servers (horizontal)serverserverservermore features → add modulesmodulemodulemodule+ new module
两条增长轴:向右——更多服务器,向下——更多模块。
QualityScalability· 横向与模块化
是什么
负载和功能都能增长,而不用重写的能力。
为什么
成功的系统会增长——边跑边重写,代价很高。
例子
负载均衡器后面的无状态服务:负载涨了——再加三个副本。
陷阱
只有一百个用户,却按一百万用户来建。
关联
建立在 State(无状态)和 Cohesion & Coupling(模块)之上。

Reliability — 系统出故障时的表现

好船不是不会沉的船(那样的船不存在)——而是有十个隔舱的船,一个被撞破了,其余的仍然浮着。

系统的每个部分终究都会坏。Reliability(可靠性)不是"永远不坏"——而是"坏了也能扛住"。架构师不试图阻止故障——而是设计系统怎样扛过故障:故障隔离、重试、优雅降级、fallback。首要规则:一个组件的故障,不能搞垮整个系统。

bulkheads: one breach stays localokokbreachsealed offokokokIsolationcontain failureRetriestry againFallbackplan BDegradationwork partly
船的隔舱:一处破口不会沉掉其他部分。下方——四种可靠性策略。
QualityReliability· 容错
是什么
系统某一部分坏了之后,整体怎么表现。
为什么
一切都会坏——唯一的问题是一处故障会不会拖垮其他部分。
例子
推荐服务挂了——商店显示"热门商品",而不是个性化推荐,但照常卖货。
陷阱
仿佛故障不会发生一样地去建系统。
关联
重试需要 Idempotency;Observability 让你看见故障。

Observability — 看不见的东西,你修不了

一辆车,没有油量表、没有温度计、没有机油压力指示灯。一路开到彻底趴窝。

Observability(可观测性):系统必须讲出自己的状态——日志(logs)、指标(metrics)、链路追踪(traces)。凌晨三点出了问题,你只有几分钟去找原因。如果一开始没有设计可观测性,事后再加就晚了——你连该看哪里都不知道。

SystemLogswhat happenedMetricshow much / how fastTraceswhere time wentYou, at 3 a.m.find it in minutes
日志、指标、追踪三种信号,把一次深夜故障变成几分钟的排查。
QualityObservability· 日志、指标、追踪
是什么
根据系统发出的信号,弄清系统内部正在发生什么的能力。
为什么
没有信号,查故障只能盲目地找上好几个小时。
例子

指标显示凌晨 03:00 错误激增,追踪显示某个外部 API 很慢,日志显示具体是什么错误。

陷阱
等第一次大故障之后才去加日志。
关联
Reliability 和 Performance 的眼睛;Security 里审计的基础。
§ 07

系统会活很久。它一定会被改。好的架构,就是容易改的架构。

Evolvability — 为变化做好准备,比今天正确更重要

在一座新城里,马路修得比今天需要的更宽。十年后车多了,窄路就得拆掉重修。

架构师不试图预测未来的每一个功能(做不到)。他们留出余地,让新功能能加进来而不破坏现有的东西。这种性质叫 evolvability(可演进性)。实践中意味着:边界清晰、契约可扩展、把"做什么"和"怎么做"分开。

QualityEvolvability· 为变化做好准备
是什么
在系统里增加或修改东西有多便宜。
为什么
需求一定会变——只是不知道会怎么变。
例子
新增一种支付方式,是加一个新的 adapter,而不是在二十个地方改代码。
陷阱
试图猜出未来的每个功能,并提前建好。
关联

来自 Interface、Cohesion & Coupling,以及可扩展的 API Contract。

从旧房子搬到新房子,是个持续几个月的过程。你不能把东西全扔了再全买一遍。

当系统需要改变时(新数据库、新协议、新数据模型)——这不是"从头重写",而是"带着系统走完一段过渡"。这就是 migration(迁移)。真实的系统在变更期间一直在运行。架构师从一开始就考虑迁移的路径,否则过渡就成了不可能的事。

Phase 1Old system100% trafficNew system0%Phase 2Old system50%New system50%Phase 3Old system0% → deleteNew system100% trafficthe system keeps running the whole time
新旧并存,流量逐步迁移,系统从不停机。
PracticeMigration· 过渡期
是什么
把一个正在运行的系统,一步一步从旧搬到新。
为什么
真实的系统不能为了翻修而关掉。
例子
换新数据库:先双写,再改成从新库读,最后关掉旧库。
陷阱
"一个季度就能从头重写"——结果同时养着两套系统两年。
关联
Data Model 和 API Contract 怎么改;Technical Debt 怎么还。

YAGNI & Deletion — 删除比添加更重要

衣柜不能无限扩大。有时必须扔掉旧的,才能给新的腾地方。

任何成功的系统都会积累死代码、没人用的功能、过时的集成。这是每个开发者每天都在交的税。 架构师会定期问:"我们能删掉什么?"——有时这就是一年里最有用的架构决策。

最结实的墙,是从来不用拆的墙,因为后面没有被塞进任何多余的东西。

有时正确的架构决策,是不做架构决策。不要"为了将来"引入一层抽象。不要"以后某天会用到"就拆分单体。不要"以防万一"就建一套插件系统。这条原则叫 YAGNI —— You Aren't Gonna Need It(你不会需要它)。好的架构师知道什么时候该说"先不做"或"我们不需要这个"。

PrincipleYAGNI & Deletion· You Aren't Gonna Need It
是什么
现在不需要的就不要建,不再需要的就删掉。
为什么
最好的架构是不需要的那一个:成本为零。
例子

删掉了一个只有两个客户在用的集成:少了一千行代码,少了一个依赖,少了深夜的告警。

陷阱
"以防万一"的抽象和插件。
关联
Simplicity 的延伸;为 Evolvability 腾出空间。
§ 08

没人理解的架构等于不存在。一半的工作,是把它传达出去。

ADR — 架构决策要写下来,否则就丢了

口头的遗嘱,随立遗嘱的人一起消失。写下来的,能传好几代人。

每一个重要的架构决策都需要记录:选了什么、有哪些备选、为什么否掉、接受了哪些妥协。这份文档现在用不上——它是一年后用的,那时来了新人,问"为什么这里做得这么怪?"标准格式是 ADR(Architecture Decision Record,架构决策记录)。

ADR-007: Use PostgresContextDecisionAlternativesWhy not themTrade-offsRevisit whenwritten now,read a yearlater
每个决策一页短文:背景、选择、备选、取舍、何时重新审视。
ToolADR· Architecture Decision Record
是什么
对一个架构决策及其理由的简短记录。
为什么
一年后没人记得为什么。记录记得。
例子

"ADR-007:用 Postgres,不用 MongoDB。我们需要订单与支付之间的事务。代价——更严格的表结构。如果出现非结构化数据,再重新审视。"

陷阱
只写"是什么",不写"为什么",也不写被否掉的选项。
关联
里面装着 Trade-offs、Technical Debt 和不可逆的决策。

C4 Model — 一张图胜过千言万语

用文字描述怎么走到一栋房子,又长又常常没用。给人看一张地图,一秒钟就够。

架构师手边总有一张系统的图:有哪些组件、怎么连接、边界在哪里。不求完美——能用就行。它用来向新人快速解释,也让你能看清系统的全貌。**C4 模型(Context、Container、Component、Code)**简单,对大多数情况已经够用。

zoom in →Contextsystem + usersContainerapps · databasesComponentparts of an appCodeclassesone diagram per zoom level — not one giant picture
四个缩放层级——像地图一样:国家、城市、街道、房子。
ToolC4 Model· Context · Container · Component · Code
是什么
四个层级的图——从整个系统一直到代码。
为什么
每类读者各看各的缩放层级,而不是一张图包打天下。
例子
Context:商店、顾客、支付服务商。Container:网页应用、API、数据库、队列。
陷阱
一张巨大的图,所有东西都画在同一层,什么也看不清。
关联
把 Decomposition 及其边界画出来;与 ADR 互补。

好医生用病人听得懂的话解释诊断。坏医生满口医学术语,病人点点头,什么也没听懂。

如果架构师只会向其他工程师解释决策,他们就丢掉了一半的工作。业务、产品、法务、安全——所有的干系人(stakeholders)都需要理解后果。把技术决策翻译成业务语言(成本、风险、时间),是架构师的核心技能之一。

对什么请求都答应的外科医生,会失去病人。好的外科医生会在手术会致命时拒绝。

团队常常提出听起来合理、却会造成长期问题的要求。架构师必须敢于反驳、解释风险,有时还要守住底线——即使不受欢迎。同时也要——敢于承认自己错了,当反方的论据更有力时,改变决定。

PracticeStakeholder Communication
是什么
用听者的语言解释决策:成本、风险、时间。
为什么
没人理解的决策,不会得到支持——或者会被推翻。
例子

不说"我们需要消息队列",而说"没有它,邮件服务一出故障,我们就会丢订单——每月大约这么多。"

陷阱
满嘴术语——或者为了避免争吵什么都答应。
关联
建立在 Trade-offs、ADR 和 C4 Model 之上。
§ 09

架构师在任何决策之前、以及一天结束时都要过一遍的检查清单。还有本课所有概念,汇在一页纸上。

任何架构决策之前
  • 究竟在解决什么问题? 不是被提出来的任务,而是它背后的问题。
  • 有哪些备选? 至少三个,包括"什么都不做"。
  • 这个选择让我失去了什么? 至少说出两个缺点。
  • 这个决策有多可逆? 可逆的——快速定。不可逆的——多想想。
  • 两三年后这会是什么样子? 不是理想的样子——是现实的样子。
  • 除了我,还有谁需要理解这个决策? 以及我该怎么向他们解释。
看任何一个系统时
  • 这个系统里最脆弱的是什么? 负载一上来,什么最先坏?
  • 哪些改动容易,哪些痛苦? 这就是衡量架构质量的尺子。
  • 每个重要事实的真相放在哪里? 它的拷贝又在哪里——缓存、副本、投影。
  • 组件之间哪些边界是明确的,哪些是偶然形成的? 偶然形成的就是技术债。
  • 每个外部依赖坏了会怎样? 一个一个地过。
  • 如果今天从头开始,我会做哪些不同的事? 那就是演进的方向。
你在像架构师一样思考的迹象
  • 你花在讨论和设计上的时间,比写代码更多。
  • 你常在问"怎么做"之前先问**"为什么"**。
  • 你把取舍大声说出来,而不是试图藏起来。
  • 你写文档和图比大多数人都勤。
  • 你看到整个系统,并能把任何一部分放在整体里讲清楚。
  • 你会想到一年后要在这上面工作的人——并把东西写得让他们用着舒服。
  • 你偶尔会因为用不上,而拒绝一项漂亮的技术。

这些概念如何相连

一个工具箱:没有钉子,锤子没用;没有锤子,钉子也没用。

本课的概念不是孤立存在的。一个决策从 Problem Framing 开始,当作 Trade-off 来权衡,检查它的 Reversibility,再写进 ADR。系统用 Decomposition 来切,切得好不好用 Cohesion & Coupling 来评判,各部分在 Interface 处相遇。数据的拷贝住在 Single Source of Truth 之下,异步世界里的重试则要求 Idempotency。

DecideProblem framingTrade-offsReversibilityADRweigh optionscost to undo?write it downStructureDecompositionCohesion / couplingInterface / contractMonolith → microjudge bymeet atlater splitData & runtimeSource of truthCachecopied toSync vs asyncIdempotencyretries needneeds
三条链:如何做决策,如何切分系统,如何与数据和故障共处。

速查表:全部 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

用成本、风险和时间来说话;该反驳时反驳,错了就承认。

架构师不是知道得更多的人。而是在开始之前想得更久的人。

速成课结束 · 9 章 · 31 个概念

实践在后面。每一章都能展开成书:Designing Data-Intensive Applications、Clean Architecture、Software Architecture in Practice、System Design Interview。但在读它们之前——先问自己这份文档里的问题。它们比答案更重要。