3、设计评审:架构设计评审要点、详细设计评审清单、评审流程与角色分工
设计评审这事儿,我做了十几年,踩过的坑比走过的路还多。说实话,很多团队把评审当成走过场,PPT一放,大家点点头就过了。结果呢?后期改代码改到怀疑人生。今天我就把压箱底的经验掏出来,跟你聊聊怎么把设计评审做实。
3.1 架构设计评审要点
架构评审,说白了就是看你的房子地基打得牢不牢。我见过太多项目,功能跑得挺好,一上压力就崩。为什么?架构层面就没扛住。
核心原则:架构评审不是找茬,是提前发现风险。别等到代码写完了才发现方向错了。
3.1.1 模块划分与职责边界
我个人习惯,先看模块划分是否清晰。每个模块该干什么,不该干什么,必须一清二楚。举个例子,我曾经在一个项目中,把日志模块和业务逻辑模块混在一起,结果后来想换日志库,改得我头皮发麻。
- 高内聚低耦合:模块内部功能要集中,模块之间依赖要少。你想想看,一个模块改了,三个模块跟着改,这架构能好到哪去?
- 接口定义:接口参数、返回值、错误码,必须明确。我建议在评审时,把每个接口的调用场景都过一遍。
- 数据流方向:数据从哪来,到哪去,经过哪些处理,画个图一目了然。
3.1.2 资源管理与性能预估
嵌入式系统资源有限,这是老生常谈了。但每次评审,我都能发现有人忽略这一点。
| 评审项 | 检查要点 | 常见问题 |
|---|---|---|
| 内存分配 | 静态分配 vs 动态分配,栈大小是否合理 | 动态分配导致碎片,栈溢出 |
| CPU占用率 | 任务周期、中断频率、临界区长度 | 中断处理时间过长,导致任务饿死 |
| 外设带宽 | SPI/I2C速率、DMA使用情况 | 轮询方式占用CPU,影响实时性 |
我记得有一次评审,一个同事设计的任务调度,中断里做了大量浮点运算。我当时就问了一句:「你这个中断频率是多少?」他说10kHz。我说:「你算算,10kHz的中断,每次处理100微秒,CPU就100%被吃掉了。」后来改成DMA+任务处理,问题才解决。
3.1.3 容错与异常处理
嗯,这里要注意。很多架构设计只考虑正常流程,异常情况全忽略。我见过一个产品,看门狗复位后,系统直接死锁。为什么?因为复位后没有初始化全局变量。
- 看门狗策略:喂狗位置要合理,不能在主循环里喂,否则死循环了还在喂。
- 异常恢复:系统崩溃后,能否自动恢复?恢复后数据是否完整?
- 错误传播:底层错误如何上报到上层?不能直接吞掉错误。
避坑指南:我曾经在一个项目中,底层驱动返回错误码,上层直接忽略。结果设备运行三天后,数据全乱了。从那以后,我要求所有错误码必须被处理,哪怕只是打印日志。
3.2 详细设计评审清单
架构评审过了,接下来就是详细设计。这部分更细,更磨人。我一般会准备一份清单,逐项检查,避免遗漏。
3.2.1 数据结构与算法
数据结构选型,直接影响性能和内存。我建议在评审时,把每个数据结构的大小、访问频率、生命周期都列出来。
- 数组 vs 链表:固定大小用数组,动态增减用链表。但链表有内存碎片风险。
- 队列与缓冲区:环形缓冲区是否考虑溢出?读写指针是否原子操作?
- 查找算法:数据量小用线性查找,数据量大用哈希或二分查找。
3.2.2 状态机设计
嵌入式系统里,状态机无处不在。我个人的经验是,状态机一定要画图,光看代码容易晕。
// 一个简单的状态机示例
typedef enum {
STATE_IDLE,
STATE_INIT,
STATE_RUN,
STATE_ERROR
} system_state_t;
system_state_t current_state = STATE_IDLE;
void state_machine_run(void) {
switch (current_state) {
case STATE_IDLE:
if (start_condition) {
current_state = STATE_INIT;
}
break;
case STATE_INIT:
if (init_done) {
current_state = STATE_RUN;
} else if (init_failed) {
current_state = STATE_ERROR;
}
break;
// ... 其他状态
}
}
评审时要检查:所有状态是否都有入口和出口?非法状态如何处理?状态转换是否有遗漏?
3.2.3 接口与协议设计
模块之间的接口,一定要定义清楚。我见过最坑的是,两个模块用同一个全局变量通信,后来改了一个模块,另一个模块莫名其妙出问题。
- 函数接口:参数顺序、类型、范围,都要明确。我建议用结构体传参,方便扩展。
- 通信协议:帧头、帧尾、校验、超时,一个都不能少。
- 回调函数:回调的上下文、执行时间、是否可重入,都要考虑。
小技巧:接口设计时,尽量让调用者简单。比如,封装一个「发送数据」的函数,内部处理加帧头、校验、重发等细节。调用者只管传数据就行。
3.3 评审流程与角色分工
评审不是一个人说了算,也不是一群人瞎聊。得有流程,有角色,有产出。
3.3.1 评审流程
我一般把评审分成三步:预审、正式评审、跟踪闭环。
- 预审:提前把设计文档发给评审人,给2-3天时间看。别搞突然袭击,没人能在会上当场看懂复杂设计。
- 正式评审:会上只讨论预审时发现的问题,不现场看代码。控制在1-2小时,时间长了效率低。
- 跟踪闭环:评审后的问题,要有责任人、截止时间。下次评审前先检查是否关闭。
3.3.2 角色分工
每个角色职责不同,不能混为一谈。
| 角色 | 职责 | 注意事项 |
|---|---|---|
| 主持人 | 控制节奏,确保讨论不跑偏 | 不要自己参与技术争论 |
| 设计者 | 讲解设计,回答问题 | 不要辩护,虚心接受意见 |
| 评审员 | 发现问题,提出建议 | 关注设计本身,不纠结代码风格 |
| 记录员 | 记录问题、决议、待办项 | 会后24小时内发出纪要 |
我记得有一次,主持人自己下场跟设计者吵起来了,结果评审会开了三个小时,一个问题都没解决。从那以后,我要求主持人必须保持中立,只负责流程。
3.3.3 评审产出物
评审不能白开,得有东西留下来。
- 评审纪要:问题列表、决议、待办项、责任人、截止时间。
- 修改后的设计文档:评审后必须更新文档,不能口头说改就完了。
- 评审结论:通过、有条件通过、不通过。有条件通过的要明确条件。
避坑指南:我曾经见过一个团队,评审结论写「有条件通过」,但条件是什么没人记得。后来项目延期了,才发现那个条件根本没满足。所以,有条件通过的条件,必须写清楚,并且跟踪闭环。
好了,设计评审这块,我就说这么多。说白了,评审不是为了找谁的问题,是为了让项目少走弯路。你想想看,一个评审会花2小时,可能省下后面2周的返工时间,这笔账怎么算都划算。