架构评审
概览
架构评审是在系统设计实施前评估系统设计的关键过程,帮助减少大规模系统中的风险,错误的早期决策可能会造成数百万美元的损失和数月的修复时间。这项技能提供了一个全面的框架,用于进行系统性评审,评估需求、可扩展性、安全性、可维护性和运维考虑因素。它使团队能够做出明智的架构决策,支持长期系统健康和业务目标。
为什么这很重要
- 减少技术债务:有效的架构评审可以预防成本高昂的返工和债务积累
- 提高系统稳定性:在生产前识别潜在的设计缺陷,减少停机时间和运维问题
- 提高团队速度:提供清晰的设计指导,帮助开发团队更高效地工作
- 降低维护成本:主动解决否则需要昂贵修复的问题
- 确保投资信心:给高管和利益相关者信心,技术投资是合理的
核心概念
1. 评审触发点
架构评审应在关键决策点触发:
- 项目启动:新项目或重大举措
- 技术变更:重大技术栈变更或框架迁移
- 重大修改:影响系统结构的架构变更
- 定期健康检查:现有系统的定期评审(季度或半年)
- 事后评审:重大事件后的评审,以防止再次发生
2. 评审类型
不同类型的评审服务于不同的目的:
- 设计评审:在实施前评估提议的架构
- 代码评审:检查实施质量和与设计的一致性
- 实施后评审:部署后评估结果
- 定期健康检查:持续监控架构健康
- 安全评审:专注于安全方面的评估
3. 检查表框架
全面的检查表确保一致的评审质量:
- 需求:功能和非功能需求是否清晰?
- 可扩展性:系统能否处理预期的增长?
- 安全性:安全措施是否充分和适当?
- 可维护性:设计是否可维护和可演化?
- 可测试性:系统是否可测试?
- 成本:成本是否合理和有理由?
- 运维:设计是否可操作和可监控?
- 技术选择:技术选择是否合理?
4. 决策框架
建立清晰的决策流程:
- 提前定义决策标准
- 记录所有考虑的替代方案
- 记录决策的理由
- 分配行动项的所有权
- 设置后续时间表
快速开始
- 启动评审:创建评审请求,附带架构文件、图表和需求
- 准备材料:准备C4图表(上下文、容器、组件)、序列图和ADR;提前48小时分享材料
- 组建评审团队:邀请架构师(领导)、技术领导、安全工程师、运维工程师、产品负责人和开发人员代表
- 进行评审:按照议程 - 15-30分钟演示,30-45分钟问答,15分钟决策和行动项
- 记录结果:记录状态(批准/拒绝/推迟)、决策、关注点和带有所有者和截止日期的行动项
- 后续行动:跟踪行动项直至完成;如需要,安排后续评审
- 关闭评审:当所有行动项解决后,标记评审为完成;记录经验教训
生产检查表
- [ ] 创建架构图表(C4模型 - 上下文、容器、组件)
- [ ] 文档化设计决策(ADR)
- [ ] 安排评审和邀请利益相关者
- [ ] 准备演示(15-30分钟)
- [ ] 完成安全评审(OWASP Top 10,威胁建模)
- [ ] 定义性能需求
- [ ] 文档化可扩展性计划
- [ ] 识别故障模式(单点故障)
- [ ] 定义运维需求(监控、告警)
- [ ] 技术选择合理化与替代方案
- [ ] 完成成本分析
- [ ] 分配带有所有者和截止日期的行动项
- [ ] 如有需要,安排后续评审
反模式
- 过度工程化:对小项目进行复杂评审,简单的评估就足够了
- 自行车棚效应:在小细节上花费过多时间,而忽略了关键问题
- 缺少后续行动:未能跟踪行动项至完成,破坏了评审的价值
- 缺乏文档:未能清晰记录决策和理由,导致重复讨论
- 忽视上下文:在不考虑项目特定约束和需求的情况下应用僵化标准
- 一刀切:对所有项目使用相同的评审深度和流程,而不考虑复杂性
集成点
- 架构决策记录(ADR):将评审与ADR中记录的特定决策链接
- CI/CD管道:将评审要求集成为合并代码前的门禁
- 文档平台:Confluence、Notion、GitHub Wiki用于存储评审报告
- 图表工具:Structurizr、C4-Model、Mermaid.js、PlantUML用于架构可视化
- 项目管理:Jira、Azure DevOps用于跟踪评审行动项
- 安全流程:作为评审一部分的威胁建模、安全审计
进一步阅读
评审流程和工作流
流程图
1. 请求评审
↓
2. 准备材料
↓
3. 安排评审
↓
4. 进行评审
↓
5. 文档决策
↓
6. 后续行动
↓
7. 关闭评审
准备检查表
对于演示者:
- [ ] 创建架构图表(C4模型)
- [ ] 文档化设计决策(ADR)
- [ ] 准备演示(15-30分钟)
- [ ] 列出开放问题
- [ ] 提前48小时分享材料
对于评审者:
- [ ] 提前评审材料
- [ ] 准备问题
- [ ] 研究不熟悉的技术
- [ ] 评审类似过去的项目
参与者和角色
评审团队
架构师(主要评审者)
- 评估整体设计
- 确保与标准对齐
- 识别架构问题
技术领导
- 评估实施可行性
- 评审技术选择
- 估计工作量
安全工程师
- 评审安全方面
- 识别漏洞
- 确保合规
运维工程师
- 评估运维复杂性
- 评审部署策略
- 评估监控方法
产品负责人
- 验证需求对齐
- 评估业务价值
- 优先考虑问题
开发人员代表
- 提供实施视角
- 提出澄清问题
- 识别潜在问题
评审文档
评审报告模板
# 架构评审:[项目名称]
**日期:** 2024-01-15
**评审者:** 爱丽丝(架构师),鲍勃(安全),卡罗尔(运维)
**演示者:** 戴夫(技术领导)
## 摘要
**状态:** ✅ 批准,需小幅修改
**总体评估:**
提议的架构合理,满足需求。实施前需要解决一些小问题。
## 需求评审
✅ **功能需求:** 定义良好,可实现
✅ **非功能需求:** 明确指定
⚠️ **约束:** 预算约束可能很紧
## 架构评估
### 优势
- 清晰的关注点分离
- 可扩展的设计
- 良好的缓存使用
- 全面的监控计划
### 关注点
1. **数据库选择**(中等优先级)
- PostgreSQL可能无法处理写入繁重的工作负载
- 考虑:在负载下评估写入性能
- 所有者:戴夫
- 截止日期:2024-01-22
2. **单点故障**(高优先级)
- Redis缓存没有冗余
- 考虑:添加Redis Sentinel或Cluster
- 所有者:卡罗尔
- 截止日期:2024-01-20
3. **成本**(低优先级)
- 估计成本达到预算限制
- 考虑:识别成本优化机会
- 所有者:戴夫
- 截止日期:2024-01-25
## 决策
### 批准
- 使用PostgreSQL作为主数据库
- 使用FastAPI实现REST API
- 在Kubernetes上部署
### 推迟
- GraphQL API(第二季度重新审视)
- 多区域部署(第二阶段)
### 拒绝
- MongoDB(不符合一致性要求)
- 无服务器架构(运维复杂性)
## 行动项
1. 添加Redis冗余(卡罗尔,2024-01-20)
2. 进行数据库负载测试(戴夫,2024-01-22)
3. 创建成本优化计划(戴夫,2024-01-25)
4. 更新架构图表(戴夫,2024-01-18)
5. 为关键决策编写ADR(戴夫,2024-01-19)
## 下一步
- 解决行动项
- 如有需要,安排后续评审:2024-01-26
- 行动项完成后开始实施
## 附录
- 架构图表:[链接]
- ADR:[链接]
- 需求文档:[链接]
常见评审模式
1. 演示+问答
格式:
- 15-30分钟演示
- 30-45分钟问答和讨论
- 15分钟决策和行动项
最适合:
- 重大架构决策
- 新项目
- 复杂设计
2. 书面RFC+异步评论
格式:
- 作者编写详细的RFC
- 评审者异步评论
- 可选的同步会议讨论
最适合:
- 分布式团队
- 较不紧急的决策
- 定义明确的问题
3. 轻量级检查
格式:
- 15-30分钟快速评审
- 专注于特定方面
- 非正式讨论
最适合:
- 较小的变更
- 进度检查
- 特定问题
需要警惕的红旗
过度工程化
🚩 红旗:
- 对于小型应用使用微服务
- 为简单问题使用复杂模式
- 过早优化
- 为了技术而技术
需要问的问题:
- 我们真的需要这种复杂性吗?
- 最简单的解决方案是什么?
- 我们可以从简单开始,然后演化吗?
工程不足
🚩 红旗:
- 没有考虑规模
- 没有错误处理
- 没有监控
- 没有安全措施
- “我们稍后会添加”
需要问的问题:
- 如果这个增长怎么办?
- 如果它坏了,我们怎么知道?
- 如果有人攻击这个怎么办?
缺少非功能需求
🚩 红旗:
- 没有性能目标
- 没有可用性要求
- 没有安全考虑
- 没有可扩展性计划
需要问的问题:
- 这个应该有多快?
- 多少停机时间是可以接受的?
- 我们将有多少用户?
单点故障
🚩 红旗:
- 单一数据库实例
- 没有冗余
- 没有故障转移机制
- 关键依赖外部服务
需要问的问题:
- 如果这个失败怎么办?
- 我们有备份吗?
- 我们能在中断中生存吗?
紧密耦合
🚩 红旗:
- 服务直接相互调用
- 服务之间共享数据库
- 没有抽象层
- 硬编码依赖
需要问的问题:
- 我们可以在不影响其他组件的情况下更改一个组件吗?
- 责任是否清晰分离?
- 我们可以独立测试组件吗?
未经论证的技术选择
🚩 红旗:
- “让我们使用X,因为它很酷”
- 没有比较替代品
- 团队没有技术经验
- 没有考虑运维复杂性
需要问的问题:
- 为什么选择这项技术?
- 你考虑了哪些替代品?
- 团队有专业知识吗?
- 学习曲线是什么?
反馈提供最佳实践
要做 ✅
具体
❌ “这个设计不好”
✅ “数据库选择可能无法处理写入繁重的工作负载。考虑...”
关注问题,而不是人
❌ “你没有考虑安全”
✅ “我们应该在这个端点添加认证”
提供替代方案
❌ “这行不通”
✅ “这种方法可能存在X问题。你考虑过Y吗?”
提问
❌ “这是错误的”
✅ “你能解释这个决策背后的理由吗?”
优先反馈
✅ “关键:添加认证”
✅ “希望有:考虑添加缓存”
不要 ❌
含糊不清
❌ “我不喜欢这个”
❌ “这感觉不对”
否定
❌ “这永远不会奏效”
❌ “我们以前试过这个,失败了”
自行车棚效应
❌ 花30分钟讨论变量名
❌ 争论代码格式化
追求完美
❌ “这需要处理每一个边缘情况”
❌ “重写一切”
架构决策结果跟踪
决策日志
# 架构决策日志
| 日期 | 决策 | 状态 | 结果 | 经验教训 |
|------|----------|--------|---------|-----------------|
| 2024-01-15 | 使用PostgreSQL | 已实施 | ✅ 工作良好 | 适合我们用例的好选择 |
| 2024-02-01 | 微服务 | 已实施 | ⚠️ 比预期更复杂 | 应该从单体开始 |
| 2024-03-01 | GraphQL API | 拒绝 | N/A | REST对我们的需求更简单 |
回顾模板
# 架构回顾:[决策]
**决策:** 使用微服务架构
**决策日期:** 2024-02-01
**回顾日期:** 2024-08-01(6个月后)
## 我们期望的
- 更快的开发(独立团队)
- 更好的可扩展性
- 技术灵活性
## 实际发生的情况
- 最初开发更慢(学习曲线)
- 运维复杂性高于预期
- 调试更加困难
## 进展顺利
- 可以独立扩展服务
- 团队自主性提高
- 部署灵活性
## 不顺利
- 分布式跟踪难以设置
- 更多的基础设施成本
- 网络延迟问题
## 经验教训
- 从单体开始,稍后提取服务
- 从第一天起就投资可观测性
- 低估了运维复杂性
## 我们会再次这样做吗?
⚠️ 也许 - 有了更好的准备和工具
## 建议
- 对于类似项目:从模块化单体开始
- 如果做微服务:在DevOps上投入重金
工具
C4图表
第1级:系统上下文
┌─────────────┐
│ 用户 │
└──────┬──────┘
↓
┌─────────────┐ ┌─────────────┐
│ 系统 │─────→│ 外部 │
│ │ │ 系统 │
└─────────────┘ └─────────────┘
第2级:容器图表
┌──────────────────────────────────┐
│ 系统 │
│ ┌────────┐ ┌────────┐ │
│ │ web │───→│ API │ │
│ │ 应用 │ │ 服务器 │ │
│ └────────┘ └────┬───┘ │
│ ↓ │
│ ┌────────┐ │
│ │数据库│ │
│ └────────┘ │
└──────────────────────────────────┘
第3级:组件图表
第4级:代码图表
序列图
用户 → API:POST /order
API → 数据库:检查库存
数据库 → API:库存可用
API → 支付:处理支付
支付 → API:支付成功
API → 队列:发布订单事件
API → 用户:订单确认
队列 → 工人:处理订单
工人 → 数据库:更新库存
架构视图(4+1模型)
1. 逻辑视图(功能)
- 系统做什么
- 类图,组件图
2. 进程视图(并发性)
- 系统如何运行
- 序列图,活动图
3. 开发视图(组织)
- 代码如何组织
- 包图,模块结构
4. 物理视图(部署)
- 组件在哪里运行
- 部署图,基础设施
+1. 场景(用例)
- 用户如何交互
- 用例图,用户故事
真实评审结果示例
示例1:数据库扩展问题
发现:
设计提议为预期100K用户的电子商务平台设计单个PostgreSQL实例。
关注点:单个实例无法处理负载
讨论:
评审者:“你预计每秒有多少事务?”
设计师:“大约1000 TPS在高峰”
评审者:“单个Postgres可以处理,但增长怎么办?”
设计师:“需要时我们会添加读取副本”
评审者:“写扩展怎么办?”
设计师:“如果需要,我们可以通过用户ID进行分片”
结果:
✅ 批准,建议:
- 从单个实例+读取副本开始
- 为未来规划分片策略
- 密切监控写入负载
- 文档化扩展触发器
示例2:安全漏洞
发现:
API设计在管理端点没有认证。
关注点:关键安全漏洞
讨论:
评审者:“我在/admin端点上没有看到认证”
设计师:“哦,我们稍后会添加”
评审者:“这是一个关键的安全问题”
设计师:“你说得对,我们现在应该添加”
结果:
❌ 拒绝 - 必须在批准前修复
- 添加JWT认证
- 实施基于角色的访问控制
- 添加速率限制
- 在部署前进行安全审计
示例3:过度工程化
发现:
设计提议为3名开发人员和1000用户的简单CRUD应用设计微服务架构。
关注点:不必要的复杂性
讨论:
评审者:“为什么这个需要微服务?”
设计师:“为了可扩展性和团队自主性”
评审者:“你有3名开发人员和1000用户”
设计师:“但我们可能会增长”
评审者:“从简单开始,需要时重构”
结果:
✅ 批准,更改:
- 从模块化单体开始
- 为未来提取设计
- 在用户达到10K时重新审视架构
- 现在文档化服务边界
最佳实践
- 早期评审 - 在实施开始前
- 做好准备 - 提前分享材料
- 保持专注 - 坚持架构,而不是实现细节
- 建设性 - 提出替代方案,不仅仅是批评
- 文档决策 - 为关键决策编写ADR
- 后续行动 - 跟踪行动项
- 学习 - 进行回顾
- 尊重 - 关注设计,而不是设计师
- 时间限制 - 不要让评审拖延
- 迭代 - 评审是对话,不是一次性事件