Architecture Review
审查一个项目的软件架构是否合理,并产出一份带有优先级的、可执行的 Markdown 审查报告。
为什么这样做
架构审查的目的不是给代码贴 SOLID 标签凑数,而是找出当前设计会在未来的变更中制造麻烦的地方——循环依赖会让"改一处、动全身",职责不清的类会让每次改动都心惊胆战,逆天的依赖方向会让单元测试变得不可能。所以整个流程的重点始终是:这个问题会在什么场景下真正伤到这个项目,以及具体怎么修。找不到真实影响的"违规"不值得写进报告;反过来,只要影响清楚,即使不完全对应某条经典原则,也值得指出。
同样重要的是:好的设计要在报告里被承认。审查不是找茬比赛,肯定合理的设计决策和指出问题一样重要——这样报告才可信,用户才知道哪些地方可以放心不动。
输入与范围
项目可能以以下方式提供:
- 已上传到
/mnt/user-data/uploads/的文件/文件夹 - 用户直接指定的磁盘路径
- 当前对话中已经在处理的项目
如果用户没有指明范围,默认审查整个项目;但如果项目明显很大(数十个文件/多个模块以上),不要试图对每个文件做同等深度的审查——先做全局扫描定位问题密集区,再重点深挖,并在报告开头明确说明审查方法是"全量"还是"抽样重点审查",避免用户误以为覆盖了每一行代码。
如果用户已经点名了具体模块/类/一次设计决策,就聚焦在那里,不必勉强铺开到全项目。
工作流程
Phase 0:确定审查范围
浏览目录结构(view 项目根目录),判断项目规模和边界。如果是单体小项目,直接进入全量审查;如果是多模块/多 target/monorepo,先列出模块清单,和用户确认(或自行判断)审查范围是全部模块还是某几个核心模块。
Phase 1:建立架构地图
在评判"合理与否"之前,先搞清楚现状是什么。做法上和探索一个陌生代码库类似,但目的是为后续评估打地基,不用面面俱到:
- 通过构建文件识别技术栈:
Package.swift/Podfile/.xcodeproj(iOS/macOS)、package.json、build.gradle/pom.xml、go.mod、Cargo.toml、requirements.txt/pyproject.toml等 - 列出模块/包/target 清单,以及它们声明的依赖关系(import 语句、podspec 依赖、Package.swift 的 target dependencies 等)
- 找出模块之间的实际引用关系(用
grep/bash_tool搜索跨模块 import),和"声明的依赖"做对比——很多架构问题就藏在"声明是分层的,实际互相乱引用"这个落差里 - 如果依赖关系不算太复杂,可以用一张简单的模块依赖图(Mermaid 或纯文本箭头)把它可视化出来,尤其是要把循环依赖标出来——这类问题光靠文字描述很难让人一眼看懂,画出来比说十遍都管用
Phase 2:逐维度评估
带着以下七个维度过一遍代码。每个维度下面写的是"看什么、怎么判断有没有问题、常见坏味道",不是死板的检查表——用它们来建立判断力,而不是逐条打钩。
1. 整体架构的模块划分
- 模块边界是否清晰:一个模块对外应该只暴露"做什么"的接口,而不是"怎么做"的细节。检查模块的 public/exported 符号里有没有混入了明显是实现细节的东西。
- 高内聚低耦合:模块内部的文件/类是否真的在协作完成同一件事,还是被"方便就放一起"拼凑起来的?
- 循环依赖:A 依赖 B、B 又依赖 A(哪怕是间接的),几乎总是设计问题的信号,因为它意味着这两个模块其实没有被真正分开。
- 依赖数量和方向:一个模块被多少其他模块依赖、又依赖了多少其他模块?“万能底层模块”和“依赖一大堆东西的上层模块”都值得关注,但前者通常是合理的(比如通用工具层),后者往往是职责蔓延的信号。
2. 模块与类的职责清晰度(单一职责)
- “上帝模块/上帝类”:一个模块或类如果承担了多个不相关的职责(比如同时做网络请求、数据持久化、业务规则校验),后续任何一个职责的变更都可能牵连到其他职责的代码。行数、方法数是廉价的信号(不是唯一标准),但更可靠的判断方法是:这个类会因为几种不同的原因而被修改? 超过一种通常就值得拆。
- 命名与职责是否匹配:类名叫
XXXManager/XXXHelper/XXXUtil往往是职责发散的先兆——这类名字几乎能装下任何东西,是需要重点抽查的信号,而不是问题本身。 - 方法的职责粒度:一个方法如果需要写"首先…然后…接着…最后"式的注释才能说清楚在干什么,通常说明它在做不止一件事。
3. 业务模块划分是否合理
- 判断这个项目的业务语境后再评估,不要生搬硬套一个不适合的模板架构(比如给一个几百行的小工具类项目硬套 Clean Architecture 的四层结构,代价可能大于收益)。
- 检查划分方式的一致性:项目是按业务领域垂直切分(比如"订单模块""用户模块"各自包含自己的 UI/逻辑/数据),还是按技术层级水平切分(所有 View 一层、所有 Service 一层)?两种都合理,但混着来——一部分按业务分、一部分按技术层分——通常会让人找不到该往哪儿加代码。
- 跨业务模块的直接耦合:模块 A 的业务逻辑里直接 new 出模块 B 的具体类型并调用,而不是通过一个抽象接口或事件通信,这会让两个本该独立演进的业务纠缠在一起。
4. 依赖方向与依赖倒置原则(DIP)
这是最容易被忽视但影响最大的一条,值得重点检查:
- 高层策略性代码(业务规则、用例)是否直接依赖低层实现细节(具体的网络库、数据库、第三方 SDK 类型),而不是依赖一个抽象(协议/接口)?如果高层代码里散落着
URLSession、CoreData、某个具体第三方 SDK 的类型,这些细节的任何变化都会直接冲击业务逻辑。 - 可测试性是一个很实用的试金石:这段业务逻辑能不能在不启动真实网络/数据库的情况下被单元测试覆盖? 如果不能,通常就是因为它依赖了具体实现而不是抽象。
- 全局单例/静态访问(
XXXManager.shared满天飞)本质上也是一种隐式的强依赖——它绕过了任何依赖注入,把"这个类依赖谁"变得不可见、不可替换。
5. 其他值得一提的原则(作为补充,不必每条都强行套用)
- 高内聚低耦合(细粒度视角):第 1 条里已经从模块层面看过内聚/耦合,这里换一个更细的粒度——具体到单个类/组件。内聚:一个类的方法是不是大多数都在操作它自己的属性?如果某个方法几乎不碰这个类的任何字段,只是把别的对象的数据拿过来加工一下("依恋情结" / feature envy),通常说明这个方法本该属于别的类。耦合:一个类的改动会不会意外牵连到看起来毫不相关的其他类?常见信号包括:多个类直接读写同一份共享可变状态而不是通过明确的接口交互、构造一个对象要连带传入一长串看似无关的依赖、修改一个类的私有实现细节却导致另一个类的测试失败。这条经常和第 1、2、4 条的发现重叠——同一个问题既可以归到"模块划分",也可以归到这里,选一个最贴切的位置说明即可,不必重复写两遍。
- 开闭原则(OCP):新增一种业务场景,是通过扩展(新增一个实现)完成的,还是要去改一个已有的大
switch/if-else? - 接口隔离原则(ISP):是否存在"胖协议/胖接口",调用者被迫实现一堆自己根本用不到的方法?
- DRY / KISS:只有在重复或复杂度已经真正造成维护负担时才提,避免为了原则而原则。
6. 软件使用者视角的可用性评估
这一条经常被纯"内部结构"导向的架构审查忽略,但对库/SDK/被多个团队复用的模块来说往往比内部整洁度更重要——架构再干净,如果调用者用起来别扭或者容易用错,也是设计失败:
- 最小惊讶原则:调用方看到一个方法/类型的名字,能不能猜对它的行为?初始化和配置的方式是不是分散在好几个地方,需要翻文档才能拼凑出正确用法?
- 是否泄漏了不该暴露的实现细节(比如把内部用的第三方类型直接作为公开 API 的参数/返回值类型),导致使用者被迫感知到本不该关心的内部结构,也让未来替换实现变成一次破坏性升级。
- 错误处理方式在公开 API 里是否一致:同一个模块里有的地方用异常/
throws、有的用返回值判断、有的用回调传错误,会让调用者每次都要重新猜一遍这次该怎么处理错误。 - 如果是库/SDK:是否考虑了版本演进和向后兼容(废弃 API 有没有清晰的迁移路径)?公开 API 表面积是不是刻意收窄到调用者真正需要的部分,而不是把内部一切都设为 public?
Phase 3:iOS / Objective-C 专项检查(如适用)
如果项目是 iOS/macOS(Swift/Objective-C,CocoaPods/SwiftPM),在完成上面的通用检查之后,读取 references/ios-specific-checks.md 补充平台相关的专项检查项(CocoaPods 模块边界、协议化依赖注入、SDK 公开接口设计、异步范式一致性等),把发现的问题一并纳入报告。不要因为项目是 iOS 项目就跳过通用维度——专项检查是补充,不是替代。
Phase 4:定级、给方案、生成报告
严重程度分级:
| 等级 | 含义 |
|---|---|
| 🔴 严重 | 会导致级联修改、无法测试、或明显违反依赖方向的问题(循环依赖、上帝类、高层直接依赖具体实现) |
| 🟡 中等 | 职责不够清晰但影响可控(内聚性一般、命名误导、局部胖接口) |
| 🟢 建议 | 锦上添花的改进,不阻塞当前开发 |
对每一个 🔴/🟡 级别的发现,必须给出具体的重构建议——用户已明确要求要看到示例代码/接口设计,而不是只有"应该拆分这个类"这种空泛的判断。示例代码不需要完整可编译,用来说清楚"改成什么样子"即可;优先展示接口/协议签名的变化和职责的重新划分,而不是逐行照抄原始代码。🟢 级别的建议可以更简短,点到为止即可。
报告结构
生成 Markdown 文件,遵循以下结构(章节可以按项目实际情况增删,但严重程度分级和重构建议是核心,不能省略):
# {项目/模块名} 架构审查报告
> 一句话总体评价 + 审查范围说明(全量审查 / 抽样重点审查了哪些模块)
## 总体评估摘要
- 整体健康度的简短判断(2-3 句话,包括做得好的地方,不要只列问题)
- 问题统计:🔴 X 个 / 🟡 X 个 / 🟢 X 个
- 最值得优先处理的 3-5 项
## 一、模块划分
(按严重程度从高到低列出发现;每条包含:位置 / 问题描述 / 为什么是问题 / 影响 / 重构建议+示例)
## 二、模块与类职责清晰度
## 三、业务模块划分合理性
## 四、依赖方向与依赖倒置原则
## 五、iOS / Objective-C 专项发现
(仅当适用时包含此节)
## 六、软件使用者视角:API / 可用性评估
## 七、优先级重构路线图
按"影响大/改动小"优先的原则给出一个可执行顺序,区分:
- 可以快速修的(quick wins)
- 需要较大改动但价值高的(值得排期)
- 理想状态但当前不必强求的(记录下来,暂不建议动)
每条发现的具体格式:
### [🔴/🟡/🟢] 简短标题
**位置**:文件/模块/类
**问题**:具体描述现状
**为什么是问题**:违反了什么原则、会在什么场景下造成实际影响
**重构建议**:
```language
// 修改前后的关键差异,或新的接口/协议设计
```
注意事项
- 不要为了显得"够专业"而制造问题——如果某个模块设计得确实合理,在报告里明确说出来。
- 优先讨论架构层面的原则性问题,命名规范、代码格式这类细节不属于本审查范围,除非它严重到影响了可读性/职责判断。
- 对历史遗留代码要现实一点:区分"理想修法"和"考虑到现有约束的渐进式修法",并在建议里说明哪个是哪个,不要假装所有代码都能推倒重来。
- 审查依据是代码里能观察到的证据,不要臆测团队的历史决策动机;如果某个设计的意图不明确,在报告里说明"这里的意图不清楚,建议和作者确认",而不是替团队编一个理由。
保存输出
将最终报告保存到 /mnt/user-data/outputs/{项目名}-architecture-review.md,并用 present_files 展示给用户。