Aesthetics - 系统如何保持优雅

核心问题:

什么样的软件是优雅的?

软件的美,不是表面漂亮,而是:

复杂性被妥善安放之后呈现出来的秩序感。

Dimension 4 的目标,是训练工程师看见系统的结构气质:

  • 是否少歧义
  • 是否少意外
  • 是否少牵连
  • 是否能组合
  • 是否整体统一
  • 是否改起来顺手

1. 简洁不是少东西,而是少歧义

简陋模型:

type User = {
  role: string
  status: string
  metadata: Record<string, unknown>
}

简洁模型:

Account
Credential
OrganizationMembership
CourseAccessGrant
Enrollment

判断问题:

  1. 它减少了歧义,还是只是减少了代码行?
  2. 它表达了本质复杂性,还是把复杂性藏进约定?
  3. 新人能否快速建立正确心智模型?

核心句:

简洁不是少东西,而是少歧义。

2. 本质复杂性要表达,偶然复杂性要消除

本质复杂性来自业务本身:

  • 支付和退款
  • 权限和访问权
  • 订阅生命周期
  • 组织成员关系

偶然复杂性来自实现问题:

  • 命名含糊
  • 重复判断
  • 错误码不统一
  • 参数顺序混乱
  • 配置散落
  • 状态乱传

核心句:

本质复杂性要被表达,偶然复杂性要被消除。

3. 一致性让系统可预测

相似动作应该有相似表达:

createCourse()
publishCourse()
archiveCourse()
deleteCourse()

不要:

createCourse()
courseGoLive()
setArchived()
remove()

检查:

  • API 命名是否一致?
  • 参数顺序是否一致?
  • 错误模型是否一致?
  • 状态机表达是否一致?
  • 目录结构是否一致?

核心句:

好系统让你看过一部分,就能合理猜到另一部分。

4. 局部性让变化有地方去

坏信号:

新增兑换码访问课程,需要改 checkout、course page、mobile API、admin、worker、analytics。

好方向:

course-access/
  grants.ts
  policy.ts
  revocation.ts
  tests/

判断问题:

  1. 一个规则变化需要改几个模块?
  2. 相关规则是否放在一起?
  3. 调用方是否知道太多内部细节?
  4. 谁拥有数据写入口?

核心句:

好设计让相关变化靠近,让无关变化远离。

5. 信息隐藏保护变化边界

调用方不应该知道访问权内部细节:

canAccessCourse(accountId, courseId)

而不是:

hasPaidOrder(accountId, courseId) ||
hasActiveSubscription(accountId) ||
hasOrganizationAssignment(accountId, courseId)

核心句:

你暴露给外部的每个细节,都会变成未来改动的阻力。

6. 可组合性不是插件化

可组合性不是一上来做插件系统、规则引擎或低代码平台。

它更基本:

每个部件职责清楚、输入输出明确、没有隐藏副作用,因此能被可靠拼接。

例如:

grantAccessFromPurchase(purchaseId)
enqueueNotification(notification)
recordAuditLog(entry)

核心句:

好模块像积木,坏模块像胶水。

7. 组合优于继承

坏方向:

class Student extends User {}
class Instructor extends User {}
class Admin extends User {}

现实里一个人可以同时是讲师、学生、管理员、企业成员。

好方向:

Account
Enrollment
InstructorProfile
AdminAssignment
OrganizationMembership

核心句:

现实世界常常是关系网,不是继承树。

8. 隐藏副作用破坏组合

危险函数:

canAccessCourse(accountId, courseId)

如果它内部偷偷:

  • 写 audit log
  • 刷新缓存
  • 创建 grant
  • 发送通知

就很难组合。

核心句:

判断函数应该尽量只判断,副作用应该显式发生。

9. 概念完整性让系统可信赖

拼贴系统的坏味道:

User / Account / Member 混用
role / permission / access 混用
status: active 到处含义不同
错误码每个模块一套
后台操作有的审计,有的没有

需要统一:

  • 领域语言
  • 错误模型
  • 权限模型
  • 状态机风格
  • API 风格
  • 后台操作流程

核心句:

语言不统一,系统不可能统一。

10. Developer Flow 决定系统能否长期保持品质

好的 flow:

找到模块
  -> 改规则
  -> 补测试
  -> 跑相关测试
  -> 看日志和指标
  -> 小步发布

坏的 flow:

全局搜索
  -> 改很多地方
  -> 不知道测什么
  -> 本地跑不起来
  -> 只能 staging 手测

核心句:

正确路径应该是最省力路径。

11. 品味是对未来维护成本的提前感知

别扭信号:

  • 名字太宽
  • 参数太多
  • 布尔参数
  • 特殊分支散落
  • 可选字段太多
  • 抽象比问题更难懂
  • 新需求没有自然落点

判断问题:

  1. 它现在能跑,但未来会在哪里疼?
  2. 它降低理解成本,还是增加理解成本?
  3. 它是解决真实变化轴,还是只是看起来高级?

核心句:

品味的一半是克制。

12. 重构是恢复秩序

重构目标:

  • 改名:恢复概念边界
  • 抽规则:减少重复判断
  • 移动代码:提高局部性
  • 拆类型:承认真实差异
  • 删除假抽象:恢复直接表达

重构前先问:

  1. 我要改善什么?
  2. 现有行为有没有测试保护?
  3. 是否改变外部契约?
  4. 能不能小步提交?
  5. 中途停下系统是否仍可运行?

核心句:

好重构不是炫技,而是让系统重新变得顺手。

一页总口诀

写代码前,先问是否少歧义。
命名时,先问别人能否猜对。
加接口时,先问是否和同类一致。
放模块时,先问变化会不会局部。
暴露细节前,先问未来是否会被绑住。
做组合前,先问是否有隐藏副作用。
做抽象前,先问是否降低理解成本。
看系统时,先问是否像一个人设计的。
做重构时,先问系统是否更顺手。

最终心法

优雅的软件不是没有复杂性,而是复杂性有秩序。

Dimension 4 的目标,是让工程师不只会判断:

能不能跑?

还会判断:

好不好懂?
好不好改?
好不好组合?
好不好延续?