抽象层次 · Levels of Abstraction

"Computer science is the science of indirection." — Butler Lampson

抽象不是"省略细节",而是选择一组对当前问题足够的不变量,把其余信息封装到下一层。好的抽象 = 上层只用其接口就能正确推理,下层实现可以独立替换。坏抽象 = 上层必须知道下层细节才能用对("抽象泄露")。

非平凡点:① 抽象有正确的层数——太少则上层做太多重复工作,太多则一行业务逻辑要穿透 7 层间接调用("抽象税")。② 每加一层抽象都在压缩信息,被压掉的那部分迟早会以 bug 形态回来——所以抽象的代价是"被压掉的信息何时反扑"。③ 与佛学"假名安立"同构:抽象是有用的虚构,不是事物本身。把 TCP/IP 当成"真的有 layers"是范畴错误——layers 是我们设计的,不是网络的物理事实。

实践判别:一个抽象是否健康,看上层能否不打开下层独立调试。若每次 debug 都要 dive through 3 层 → 抽象选错了边界。换言之:好抽象让推理变线性,坏抽象让推理变树形展开。

经典例子

TCP 给上层"可靠字节流"的假象——上层 application 不需知道 IP 分包、重传、拥塞控制。这是 Internet 能 scale 的关键:每加一层(IP → TCP → HTTP → REST → GraphQL)都让开发者忘掉一整套问题。代价:抽象泄露时(如 head-of-line blocking 拖慢 HTTP/2),上层必须临时变成"半个 TCP 专家"才能调优。

场景 · BigCat

设计 AI agent 系统:把 LLM 当作"自然语言函数"是一层抽象。好处:上层用 prompt 编排即可;代价:当 LLM hallucinate 或 token cost 爆炸,你必须临时下沉到"它其实是概率采样 + context window"层去 debug。选错抽象层 = 设计成本的主要来源。教孩子也同理——"学习"是一层抽象,孩子卡住时你必须能下沉到"注意力 / 工作记忆 / 动机"等更底层概念,否则给不出有效干预。


Levels of Abstraction — abstraction isn't "hiding details," it's picking the set of invariants sufficient for the current problem and encapsulating the rest one layer down. Good abstraction: upper layer reasons correctly using only the interface. Bad abstraction: upper layer must know lower-layer details to use it (leaky abstraction). Each layer compresses information; whatever gets compressed away eventually returns as a bug. Test: can you debug the upper layer without opening the lower one? If every debug session requires diving through 3 layers, the boundary is wrong.

中文提示词
我正在设计 [系统/模块]。当前抽象边界是 [描述]。请: ① 找出 2-3 个最可能"抽象泄露"的地方——上层必须知道下层细节才能用对的接口; ② 评估是否抽象层太少(上层重复劳动)还是太多(穿透太多层 debug); ③ 给出 1 个具体的重画边界方案,说明它解决了哪个信息压缩反扑。
English Prompt
I'm designing [system/module]. Current abstraction boundaries: [describe]. Please: 1. Identify 2–3 likely leaky abstractions — interfaces where the upper layer must know lower-layer details to use them correctly. 2. Diagnose whether there are too few layers (upper-layer duplication) or too many (debug requires piercing many layers). 3. Propose one concrete re-drawing of boundaries, naming which compression-rebound it resolves.

康威定律 · Conway's Law

"Organizations design systems that mirror their communication structure." — Melvin Conway, 1967

系统架构 = 组织沟通结构的同构投影。两个 team 之间沟通成本高 → 它们负责的模块之间一定会演化出"防御性接口",无论你最初画的架构图多漂亮。这不是 bug 是社会-技术系统的物理定律

非平凡点:① 逆康威 (Inverse Conway Maneuver)——想要某种架构,先把组织重组成那个形状。要 microservices?先要"小、自治、有完整 ownership"的小队。组织没拆,强行拆服务 = 拆出一堆分布式 monolith。② 与涌现同源:架构不是设计出来的,是"在沟通带宽约束下涌现"出来的。架构师能影响的是边界条件(团队怎么分、谁向谁汇报、谁和谁同会议),不是直接的代码图。③ 对 AI 时代有非平凡推论:单个 AI 增强的开发者 = 把团队压缩成 1 → 康威定律说他/她的代码会少很多"防御性边界",结构更紧致——但也更难被他人接管。

实践:设计架构前先画组织通信图——谁每天和谁说话?谁向谁汇报?这张图比任何 UML 都更精准地预测 6 个月后的代码长什么样。

组织沟通结构 CTO Team A Team B Team C 弱连接 = 防御性接口 同构投影 系统架构 API Gateway Svc A Svc B Svc C 弱连接 = 厚重 API + duplication 想要新架构 ⇒ 先重画组织通信图(逆康威)
康威定律:架构 = 组织通信结构在代码空间的同构
经典例子

Amazon 的 "two-pizza team" + "API-only mandate"(Bezos 2002 备忘录)是经典逆康威。Bezos 没说"我们要建 microservices 架构",他说"任何团队对外通信必须通过 service API"。组织通信结构被强制改造 → 6 年后 AWS 涌现为可对外销售的产品。架构不是被画出来的,是被组织约束出来的。

场景 · BigCat

① 在公司:分布式系统里两个模块总在边界打架?看人——这俩 team 之间是不是"开会才说话"?拉近 daily 沟通比重构代码更治本。② 在家:把孩子的"练琴模块"和"作业模块"塞给同一个家长 → 边界自动模糊;分给不同家长 + 通过共享日历同步 → 自动涌现出"接口契约"。康威定律不分公司还是家。③ AI 时代:你 + 一组 agents 是一个新的"组织"——你和 agent 的对话带宽决定了产出代码/产物的耦合度。沟通带宽窄 = agent 输出会变碎片化。


Conway's Law — system architecture is an isomorphic projection of the organization's communication structure. High-friction team boundaries become defensive API boundaries in code, regardless of the original architecture diagram. The Inverse Conway Maneuver: to get the architecture you want, first reshape the org. Architecture isn't designed — it emerges under communication-bandwidth constraints. The architect's lever is boundary conditions (team splits, reporting lines, who shares meetings), not the box diagram. Map team-communication structure before drawing UML — it predicts the code 6 months out more accurately than any design doc.

中文提示词
我们团队当前组织结构是 [描述],想要的目标架构是 [描述]。请: ① 用康威定律预测:在当前组织下,6 个月后实际会涌现出什么样的架构? ② 找出 2 个最可能演化为"防御性接口"的团队边界; ③ 给出 1 个最小化的"逆康威"动作(重组哪条沟通线、谁开始同会),它能让目标架构变得可行。
English Prompt
Our team org structure: [describe]. Target architecture: [describe]. Please: 1. Apply Conway's Law: under the current org, what architecture will actually emerge in 6 months? 2. Identify 2 team boundaries most likely to harden into defensive APIs. 3. Propose one minimal Inverse-Conway move (which communication line to redraw, who joins which meeting) that makes the target architecture feasible.

技术债 · Technical Debt

Ward Cunningham 1992 隐喻——并非所有债都坏,关键是"利息率"和"还款意图"

原始隐喻被严重误用:"hacky 代码 = 技术债"是错的。Cunningham 原意:带着"现在的不完全理解"先发布,把发布后学到的洞见再 refactor 回来——这是"借"。利息 = 每次 feature 都在错误模型上多写一点,未来 refactor 成本指数上升。

非平凡点:① 有意识的债 vs 无意识的债——前者是策略(赶 deadline、先验证 PMF),后者是无知(团队根本不知道有更好做法)。后者最危险:你不知道自己在欠债。② 利息率不是匀速的——某些债 5 年不爆(dead code、风格不一),某些债 6 个月就让团队走不动(错的数据模型、错的核心抽象、错的部署架构)。优先还高利率债,不是"看起来最丑的债"。③ 所有抽象选择都是借钱——零债务 = 零产出。健康团队 = 显式跟踪自己借了什么、利率多少、什么时候打算还。④ 与佛学"业 (karma)"结构同构:每个 commit 在 codebase 里种下一个未来必须面对的果——但你可以觉察并主动还,而不是被动承受。

实践:每季度列"技术债账本"——3 列:欠了什么 / 估计利率 / 计划还款月。没上账本的债 = 高利率债

经典例子

Twitter 早期 Rails monolith——快速验证 PMF 的正确债务选择。但 2010 年 World Cup 期间反复宕机时,债务利率已变成"每多一个用户就指数上升"。他们用 3 年时间重写为 Scala/JVM 微服务("Fail Whale"时代)。教训:早期借得对(让 Twitter 活下来),中期没及时还(拖到危机才动)。

场景 · BigCat

① 个人知识系统的债:随手记笔记不归类 = 借了"分类决策"债,3 个月内利率低(笔记少时随便搜),1 年后变高利率(笔记爆炸时找不到东西)。② 育儿的"技术债"也成立——为了赶时间替孩子做决定 = 借"她的自主性"债,利率会在青春期一次性兑现。③ AI 工作流:prompt 复制粘贴 = 借"prompt 系统化"债。最初提速,半年后所有 prompt 都不能复用、版本不可追溯 = 你在为一年前的偷懒付高利息。关键不在不借,而在记账。


Technical Debt — Cunningham's original metaphor is widely misused. It's not "hacky code." It's: ship with incomplete understanding, then refactor in what you learned post-release — that's the borrow. Interest = every new feature built on a flawed model compounds future refactor cost. Distinguish conscious debt (strategy) from unconscious debt (ignorance) — the latter is dangerous because you don't know you're indebted. Interest rates vary wildly: some debt sits 5 years (dead code), others kill velocity in 6 months (wrong data model, wrong core abstraction). Pay down high-rate debt first, not the ugliest debt. Zero debt = zero output; healthy teams track what they borrowed, the rate, and when they plan to pay.

中文提示词
我们的项目/系统是 [描述]。请帮我做技术债审计: ① 列出 5 项当前存在的债,按"利率估计 × 当前余额"排序; ② 标记每项是"有意识借的"还是"无意识欠的"——后者优先; ③ 给出未来 1 个季度的"还款计划":还哪 2 项、为什么是这两项、能换回多少未来 velocity。
English Prompt
My project/system: [describe]. Run a technical-debt audit: 1. List 5 current debts, ranked by estimated interest rate × current balance. 2. Tag each as "consciously borrowed" or "unconsciously incurred" — prioritize the latter. 3. Propose a 1-quarter repayment plan: which 2 to pay down, why these two, how much future velocity it buys back.

YAGNI 与过早抽象 · YAGNI & Premature Abstraction

"You Ain't Gonna Need It" — XP 原则 / "Duplication is far cheaper than the wrong abstraction." — Sandi Metz

YAGNI 是反"未来主义工程"的纪律:不要为想象中的需求写代码。但 YAGNI 的深层不是"少写代码",而是抽象需要数据——你必须先看到 3-5 个真实变体,才能知道正确的抽象边界。在1-2 个变体时就抽象,等于在样本不足时拟合——必然 overfit 到当前 2 个例子,然后第 3 个例子来时整个抽象塌掉。

非平凡点:① "看起来重复" ≠ "本质重复"——两段长得一样的代码可能是同一概念的 2 个 instance(应该 DRY),也可能是两个不同概念恰好暂时长得一样(不应 DRY)。提前 DRY = 把两个概念锁进一个 abstraction,未来它们各自演化时你要痛苦地把它们撕开。② Sandi Metz 名言:"duplication is far cheaper than the wrong abstraction"——错抽象的成本远高于复制代码。原因:复制代码是线性问题(多改几个地方),错抽象是耦合问题(每个新需求都被错框架污染)。③ 反方向陷阱("YAGNI 过头"):核心数据模型选错也叫"我不预测未来"——错了。架构层面的决策(数据模型、部署拓扑、核心 API 契约)必须预测,因为它们一旦错了,技术债利率最高(见 Card 3)。YAGNI 适用于实现层,不适用于架构层。④ "等三次再抽象"是 Martin Fowler 的 Rule of Three 启发法——前两次容许重复,第三次再抽象,此时样本充足。

实践:写代码时问"我现在抽象的依据是已看到的真实变体还是想象中的变体?" 后者 = YAGNI 触发,立即停。

经典例子

Knight Capital 2012——为"未来某天可能用到"留了一段 8 年没用的代码(dead code,名为 Power Peg)。一次部署疏忽把它重新激活,45 分钟内交易系统狂下错单亏损 4.4 亿美元,公司次日破产。YAGNI 不是风格洁癖:每行你"以为不会执行"的代码都是一颗定时炸弹。把"未来可能要"的代码留在仓库 = 留下被未来意外触发的爆炸装置。

场景 · BigCat

① 工程:构造 AI agent 框架时,看到 2 个 agent 都有"retry + 日志"逻辑,立即抽 BaseAgent?停。等第 3 个 agent 出现再看 3 个共性是什么——你会发现前 2 个的"retry"其实语义不同(一个是 API 限流,一个是 hallucination 重采样),强抽到一起只会把两种重试纠缠在一个错的接口里。② 育儿:孩子第 1 次说"我不想去学钢琴",立即构造一整套"如何让她持续坚持"的体系 = 过早抽象(样本 = 1)。先观察 3-5 次,分清是"今天累了"、"老师问题"、还是"真的不喜欢"——3 个根因要 3 套策略,提前抽象 = 用一把锤子敲所有钉子。③ 知识库:刚学 5 篇神经科学论文就建立"我的神经科学体系" = 过早抽象,前 10 篇的 framework 几乎必然要推翻。读到 30-50 篇再画地图。"复制 3 次再抽象"是抗 overfit 的工程版本。


YAGNI & Premature Abstraction — XP's "You Ain't Gonna Need It." Deep version: abstraction needs data — you must see 3–5 real variants before the right boundary is visible. Abstracting at variant #1 or #2 = fitting on insufficient samples; you'll overfit and the abstraction collapses when variant #3 arrives. Sandi Metz: "duplication is far cheaper than the wrong abstraction." Reverse trap: don't push YAGNI into architecture-level decisions (data model, core APIs, deployment topology) — those have the highest interest rate when wrong. YAGNI applies to implementation layer, not architecture layer. Fowler's Rule of Three: tolerate duplication twice; abstract on the third occurrence when samples are sufficient.

中文提示词
我正在考虑给 [代码/系统/流程] 加一层抽象/通用框架。请压力测试: ① 我目前手上有几个真实出现过的变体?少于 3 个 → YAGNI 触发,请说服我先复制; ② 如果硬抽象,最可能 overfit 到哪个特性,将来第 3 个变体出现时如何 break? ③ 区分这个决策属于"实现层"还是"架构层"——若是架构层,给出必须现在决定的最小决策集。
English Prompt
I'm about to add an abstraction / generic framework to [code/system/process]. Stress-test me: 1. How many real observed variants do I have? If < 3 → YAGNI trigger; argue me into duplicating first. 2. If I abstract now, which feature am I most likely overfitting to, and how does it break when variant #3 arrives? 3. Classify the decision as implementation-layer vs architecture-layer. If architecture-layer, name the minimal set of decisions that must be made now.