| name | architecture-review |
| description | Critically evaluate whether a code project's architecture is sound — module decomposition/cohesion, module and class responsibility clarity (SRP), whether business logic sits in the right modules, dependency direction and Dependency Inversion Principle (DIP) adherence, and whether the public API is intuitive and hard to misuse for its consumers (crucial for SDKs/libraries). Produces a structured Markdown report with severity-ranked findings and concrete refactoring suggestions (example code / interface redesigns). Use whenever the user asks to review, audit, or critique architecture, or asks things like "架构是否合理", "模块划分是否合理", "职责是否清晰", "是否符合依赖倒置/SOLID", "这样设计好不好", "这个类是不是太重了", "SDK 好不好用" / API 设计是否合理, or wants a second opinion on a design/refactor decision. This is a critique/audit skill (finds problems, proposes fixes), unlike a plain codebase-overview skill. For iOS/macOS (Swift/OC) projects, also read references/ios-specific-checks.md. |
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 展示给用户。