怎么用这份清单
这份清单面向内容、社区与即时通信类客户端的架构评审。它不检查代码风格,也不检查框架选型,只检查七个容易出系统性问题的地方。
使用方式:
- 评审前,让方案负责人按清单准备证据。没有证据的条目一律记为「未验证」。
- 评审时,逐条确认「判据是否满足」,而不是「有没有提到」。
- 评审后,把未通过项写进风险台账,指定负责人与复核时间。
每条给出三段:判据(什么算通过)、证据(拿什么证明)、反模式(最常见的错误做法)。
第一组:面与状态归属
1.1 四条消费面是否分开?
- 判据:信息流、详情、互动、实时各自有独立的状态容器与失败策略。
- 证据:状态树或模块图;任何一条面的失败不会导致其他面白屏。
- 反模式:一个大
store装下所有内容,用一个全局 loading。
1.2 每条数据的事实源是否明确?
- 判据:列表、详情、计数、草稿、连接状态各自标注了「服务端为准」或「端上为准」。
- 证据:一张数据归属表,含冲突时的裁决规则。
- 反模式:端上和服务端都能改同一字段,没有优先级。
1.3 列表容器是否把条目、游标、已见集合绑在一起?
- 判据:三者通过同一个写入方法更新;淘汰条目时同步清理已见集合。
- 证据:容器代码与淘汰路径的测试用例。
- 反模式:游标存在组件里;淘汰只删数组不删 id。
1.4 详情页是否只依赖 id?
- 判据:从分享链接、推送、搜索、历史记录进入时行为一致。
- 证据:四条入口的验收记录。
- 反模式:从列表进入传整个对象,深链进入走另一套逻辑。
第二组:读路径
2.1 游标是否唯一确定位置?
- 判据:游标包含排序所需的全部字段,通常是
(时间戳, id)。 - 证据:分页边界用例,覆盖同一时间戳多条内容。
- 反模式:只用时间戳;用页码做内容流分页。
2.2 重复内容是否有端上去重?
- 判据:已见集合按信息流隔离;重复项不进入渲染列表。
- 证据:两次分页有交集的用例。
- 反模式:假设服务端永远不重复。
2.3 刷新是否保持位置?
- 判据:刷新只把新条目插到顶部,已有条目顺序不变;滚动位置按插入高度补偿。
- 证据:刷新前后首条可见 id 的记录。
- 反模式:刷新重建整个数组。
2.4 媒体是否有稳定占位?
- 判据:图片与视频预留宽高比;加载失败有稳定降级图。
- 证据:弱网下的布局偏移记录。
- 反模式:等图片加载完再决定高度。
第三组:写路径
3.1 每个写操作是否标注了回滚成本?
- 判据:点赞、收藏、评论、上传、支付类操作各自的乐观策略有明确依据。
- 证据:一张写路径表,含「是否乐观」「失败后保留什么」。
- 反模式:所有写操作统一乐观,或统一等待。
3.2 乐观更新是否只改对应条目?
- 判据:失败回滚不触碰正文、不重置媒体播放、不改变滚动位置。
- 证据:回滚路径的测试用例。
- 反模式:用服务端返回值替换整个内容对象。
3.3 评论失败是否保留草稿?
- 判据:失败后输入框内容不丢;重试不产生重复评论。
- 证据:断网发送与恢复后的验收记录。
- 反模式:失败即清空输入框。
3.4 上传是否可续传?
- 判据:有上传会话 id、已确认分片清单;跨进程恢复能继续。
- 证据:中断后恢复的验收记录。
- 反模式:进度按已发送字节算;完成请求不重试。
第四组:实时连接
4.1 通道选型是否有判定依据?
- 判据:按方向、延迟、连接数、运维能力逐项回答,而不是「默认 WebSocket」。
- 证据:选型记录,含被否决的方案与原因。
- 反模式:只写「用 WebSocket,因为实时」。
4.2 连接是否有显式状态机?
- 判据:
idle / connecting / open / closed至少四态;UI 只读状态,不直接读 socket。 - 证据:状态迁移图与非法迁移的处理。
- 反模式:用
socket.readyState直接驱动 UI。
4.3 重连是否带退避与抖动?
- 判据:指数退避,带随机抖动,有上限;鉴权失败不无限重试。
- 证据:服务端重启场景的连接曲线。
- 反模式:固定 1 秒重连;失败后立即重试。
4.4 重连是否带断点?
- 判据:重连带上次最大序列号;服务端从该点补发;客户端检测空洞。
- 证据:断网 30 秒后恢复的消息完整性记录。
- 反模式:重连只拉最新一条;重连清空会话。
4.5 是否有心跳与半开检测?
- 判据:心跳间隔小于代理超时;超过两个周期未收到 pong 主动关闭。
- 证据:半开连接场景的检测记录。
- 反模式:只发心跳不检测回应。
第五组:降级与失败
5.1 每条能力是否有降级路径?
- 判据:实时断开可手动刷新;图片失败有占位;推送不可用有打开应用补偿。
- 证据:降级矩阵,含触发条件与用户可见状态。
- 反模式:实时是渲染前提,断开即白屏。
5.2 降级是否可观测?
- 判据:每次降级记录一次事件,包含原因与持续时间。
- 证据:埋点或日志字段定义。
- 反模式:降级静默发生,线上无人知道。
5.3 错误是否分类?
- 判据:网络错误、服务端拒绝、校验失败、用户取消分开处理。
- 证据:错误码到 UI 文案的映射表。
- 反模式:所有错误显示「出错了,请重试」。
第六组:安全与审核
6.1 谁能写 HTML?
- 判据:作者内容、运营配置、第三方嵌入分级;不可信内容先结构化再渲染。
- 证据:sanitize 白名单与绕过测试。
- 反模式:直接
innerHTML渲染用户内容。
6.2 审核态是否与正文分离?
- 判据:列表与详情都返回状态;状态未知时先占位。
- 证据:审核中内容的渲染记录。
- 反模式:先渲染正文,等详情接口返回再替换。
6.3 拉黑是否立即生效?
- 判据:端上先隐藏相关内容,再同步服务端;失败也不回显。
- 证据:拉黑后的列表、详情、搜索三处验收记录。
- 反模式:等服务端确认后才隐藏。
6.4 举报是否留存证据?
- 判据:举报携带内容 id、快照时间、举报理由;快照不可被作者后续编辑覆盖。
- 证据:举报请求的字段定义。
- 反模式:只传内容 id,内容被删后无法复核。
第七组:可观测
7.1 关键链路是否有端上指标?
- 判据:首屏、分页成功率、写请求失败率、连接可用时长、未读同步延迟。
- 证据:指标定义与采集点。
- 反模式:只有崩溃率,没有业务链路指标。
7.2 是否能用序列号对齐端与服务端?
- 判据:消息、分页、未读都带可比较的序列号或版本号。
- 证据:一次跨端排查的记录。
- 反模式:两边时间戳精度不同,无法对齐。
评审记录模板
评审对象:
评审日期:
参与人:
通过的条目:
未通过的条目:
- 编号:
- 现状:
- 风险:
- 负责人:
- 复核时间:
未验证的条目:
- 编号:
- 缺少的证据:
- 补交时间:
本次新增的风险:
模板的重点是「未验证」单独一栏。评审最常见的失败不是「没通过」,而是「以为通过了,其实没人验证」。
一次评审示例
某次内容客户端评审,方案自评全部通过。逐条核对后发现三处问题:
4.4 重连带断点:未验证。 方案写了「重连后拉取最新消息」,但没有说明序列号。追问后发现服务端不保存消息序列,只保留最新 20 条。结论:断线超过 20 条消息就会永久丢失。风险等级高,要求服务端补序列号。
5.1 降级路径:不通过。 实时连接是详情页渲染的前提,连接失败时页面一直显示加载中。结论:需要增加「实时不可用但内容可读」的状态,实时只影响角标。
6.3 拉黑立即生效:不通过。 端上等服务端返回成功后才隐藏内容,弱网下会有几秒延迟。结论:改为本地先隐藏,失败也不回显,同步失败进入重试队列。
这次评审没有发现代码 bug,但发现了三个架构级问题。它们的共同点是:方案在正常路径上都对,在失败路径上都缺一环。清单的作用就是把评审的注意力从正常路径拉到失败路径。
小结
评审清单的价值不在条目数量,而在每条都有判据与证据。没有证据的「已支持」等于「未验证」。
七组条目里,最容易漏的是第四组与第五组:连接可靠性与降级。它们不出现在正常路径的演示里,只在断网、切后台、服务端重启时暴露。评审时应该主动要求这些场景的验收记录。
常见错误与边界
- 把清单当成打分表。通过数量不重要,未验证项才重要。
- 只评审正常路径。失败路径才是内容客户端的常态。
- 让方案负责人自评后直接通过。至少要有一个人独立核对证据。
- 把「有降级」当成「降级正确」。降级后用户是否还能完成核心任务,需要单独验收。
- 忽略多端。同一账号在两端表现不一致,通常说明状态归属有问题。
- 评审一次就结束。连接、审核、关系缓存都会随版本变化,需要定期复核。
- 把风险台账写成会议纪要。每条风险必须有负责人与复核时间。
- 清单条目越多越好。七组已经覆盖主要系统性风险,继续增加会降低执行率。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。