先把系统设计理解正确
很多 New Grad 准备系统设计的方式是错的。他们一上来就刷“设计 Twitter”“设计 Uber”这种大题,背几个 Kafka、Redis、负载均衡、微服务的词,然后努力让自己听起来像一个资深工程师。问题是,早期工程师的系统设计面试通常不是要你假装自己已经负责千万级流量的基础设施,而是看你能不能把一个模糊的产品问题讲清楚,选择简单合理的架构,解释取舍,并且知道系统可能在哪里出问题。
对 New Grad 和早期工程师来说,一个好的系统设计答案主要证明四件事:你能把模糊的产品想法拆成明确需求;你能选择和问题匹配的简单组件;你能把数据、API 和用户流程讲清楚;你能诚实讨论限制、失败场景和技术取舍。
这就够了。你不需要听起来像 Principal Engineer。你需要听起来像一个团队可以放心培养、放心交给真实功能的人:一个服务边界、一张数据库表、一个队列、一个缓存、一次调试,或者一个完整功能流。
RoleProof 系统设计评分表
用这张 100 分表判断你的系统设计答案是否已经接近面试可用。
| 信号 | 分数 | 面试官会听什么 |
|---|---|---|
| 需求澄清 | 15 | 你是否问清系统要做什么、谁在用、哪些约束重要。 |
| 用户流程 | 10 | 你是否能描述从用户动作到后端响应的主路径。 |
| API 设计 | 15 | 你是否能定义清楚的 endpoint、输入、输出和错误情况。 |
| 数据模型 | 15 | 你是否能选出核心实体、关系、ID、索引和存储需求。 |
| 架构选择 | 15 | 你是否只在必要时使用服务、队列、缓存、存储或 worker。 |
| 扩展性与性能 | 10 | 你是否能用简单估算支撑决策,而不是为了炫技而过度设计。 |
| 失败场景 | 10 | 你是否能说出哪里会坏,以及系统如何降级或恢复。 |
| 沟通表达 | 10 | 你是否能保持结构清楚,让取舍容易被理解。 |
早期候选人最容易丢分的地方有两个:跳过需求澄清,以及一开始就过度设计。最好的答案通常先从小系统开始。比如题目是“设计一个通知系统”,不要第一句话就说 Kafka、Redis、Kubernetes、Cassandra 和微服务。先问产品:谁会收到通知,什么事件触发通知,是否必须实时,发送失败怎么办,用户如何管理偏好。然后再选择能满足这些需求的最简单设计。
一个好用的开场话术
如果面试官给出题目,你可以这样开场:
在画架构之前,我想先确认产品范围。我会先确定主要用户和核心动作,然后定义 API 和数据模型,接着讨论扩展性和失败场景。这个面试里,您希望我先优化正确性和清晰度,还是直接假设这是一个高规模生产系统?
这句话有三个作用。第一,它展示结构。第二,它让面试官告诉你希望深入到什么程度。第三,它避免你在还没理解问题之前就开始表演复杂度。
第一个检查清单
练习任何系统设计题之前,先写下这七个问题的答案:
- 用户是谁?
- 用户的主动作是什么?
- 哪些事情必须立刻发生?
- 哪些事情可以异步完成?
- 必须存哪些数据?
- 哪些地方可能失败?
- 第一版你会刻意简化什么?
如果这些问题还答不出来,先不要画框图。图只会把混乱藏起来。
1. 早期工程师该怎么理解系统设计
早期系统设计和资深系统设计最大的区别,是 负责范围 的期待不同。资深工程师可能会被考察组织边界、迁移策略、成本、团队协作、长期可靠性、复杂扩展和多区域部署。New Grad 通常被考察的是:你的工程推理是否足够清楚,是否能成长为可以承担这些复杂工作的工程师。
所以你的答案要避开两个极端。
第一个极端是玩具答案。它听起来像课程项目:一个 web server,一个 database,然后就结束了。没有 API 设计,没有失败场景,没有索引,没有异步任务,没有监控,也没有取舍。面试官会觉得:这个人会写代码,但还没有开始用系统思维看问题。
第二个极端是术语答案。它在问题还不需要的时候就使用分布式系统词汇。面试官听到了工具,但听不到判断。一个候选人说“用 Kafka,因为它 scale”,却说不清为什么事件流适合这个问题,这并没有证明系统设计能力,只证明了他记住了一个名词。
最强的早期工程师答案在中间。它可以这样说:
第一版我会保持简单:一个 web client,一个 API service,一个关系型数据库存核心记录。如果涉及文件,加 object storage。如果有不需要阻塞用户的任务,比如提醒、邮件或报告生成,就用 background worker。等读流量变高,再给热点读取加缓存;等异步任务需要更强可靠性,再引入 queue,并让 worker 的处理具备幂等性。
这个答案不花哨,但可信。
2. 需求澄清:大多数候选人最容易跳过的部分
需求澄清不是热身。它是整个设计的地基。如果你理解错了产品,后面每一个技术决策都会变弱。
好的需求澄清有三层。
第一层,找出核心用户动作。如果题目是“设计一个文件分享系统”,核心动作可能是上传文件、分享给另一个用户、之后下载。如果题目是“设计一个打车匹配应用”,核心动作可能是发起请求、寻找附近司机、确认匹配、追踪状态。如果题目是“设计一个求职 tracker”,核心动作可能是保存职位、更新状态、设置后续提醒。
第二层,区分功能需求和非功能需求。功能需求描述系统做什么。非功能需求描述质量:延迟、一致性、可靠性、安全、隐私、规模、成本和可维护性。
第三层,定义不做什么。早期候选人在这里很容易加分。比如你说“我会先做单区域设计,最后再讨论多区域”,通常比一开始试图解决所有问题更好。
好用的问题包括:
- 这是读多、写多,还是读写均衡?
- 用户是否需要实时响应?
- 数据是否敏感?
- 是否可以接受最终一致性?
- 当前更重视正确性、速度、成本,还是简单性?
- 规模假设是多少:几百、几千,还是百万用户?
- 是否有合规、滥用、安全方面的约束?
你的目标不是问二十个问题,而是问足以决定设计方向的问题。
3. API 设计:清晰度最快暴露的地方
对早期工程师来说,API 设计是最快证明实践判断的部分。清楚的 API 说明你理解用户流程、后端职责、数据结构和错误处理。
以求职 tracker 为例,简单 API 可以是:
POST /applications
GET /applications?status=applied
PATCH /applications/:id/status
POST /applications/:id/reminders
GET /applications/:id
每个 endpoint 都应该有理由。POST /applications 创建保存的职位。PATCH /applications/:id/status 更新申请状态。POST /applications/:id/reminders 设置后续提醒。你不需要为每张数据库表设计一个 endpoint。你需要让 endpoint 对应真实用户动作。
强的 API 讨论会包括:
- request body 示例
- response shape 示例
- validation 规则
- authorization 检查
- 重复请求的幂等性
- 列表接口的 pagination
- 错误情况
弱的说法是:
前端调用后端,然后后端更新数据库。
这句话是真的,但几乎没有证明能力。
更好的说法是:
当用户保存一个职位时,客户端把 title、company、official URL、source、target role 和可选 notes 发到 POST /applications。服务端验证 session,标准化 URL,检查这个用户是否已经保存过同一个职位,然后写入 application row,并返回稳定的 application ID。如果同一个职位重复保存,API 可以返回已有记录,而不是创建重复数据。
这听起来就像真正做过产品工程的人。
4. 数据模型:把工程判断变成证据
系统设计答案一旦有了具体数据模型,就会强很多。你不需要写完整 SQL migration,但应该说出核心实体和关系。
还是以求职 tracker 为例:
User
Application
ResumeProfile
Reminder
ApplicationEvent
关系很简单:
- 一个 user 有多个 applications。
- 一个 user 有多个 resume profiles。
- 一个 application 可以引用当时使用的 resume profile。
- 一个 application 有多个 events:saved、viewed、applied、rejected、interview、offer。
- 一个 application 可以有 reminders。
重点不是表名是否完美,而是你能解释为什么这个模型支撑产品。
例如:
我会把 application status 做成受控 enum,这样 tracker 能稳定分组。同时我会单独存 application events,因为 status 只告诉当前状态,events 才能告诉历史变化。这样用户之后可以看出自己的转化模式。
这是强答案,因为它把 schema 设计和用户价值连起来了。
面试官喜欢听到的数据模型信号包括:
- 稳定 ID
- 时间戳
userId这种 负责范围 字段- 流程 state 的 enum
- 用于去重的 unique constraint
- 常用查询的 index
- 用 event table 保存历史
- 把核心记录和日志/派生特征分开
不要一上来就说“NoSQL,因为它 scale”,除非你能解释 access pattern。对早期工程师题目来说,关系型数据库经常是最好的第一答案,因为它清楚支持结构化产品数据、关系、事务和查询。
5. 架构:从简单开始,只在问题值得时增加复杂度
好的系统设计答案不会为了显得厉害而增加基础设施。它只在需求要求时增加组件。
一个简单版本可以是:
Client -> API service -> Database
|
-> Background worker -> Email provider
这已经足够支撑很多产品系统。API 处理用户动作。数据库保存核心状态。Worker 处理延迟任务或慢任务,比如提醒、邮件、导入、报告或较贵的分析。如果涉及文件,加 object storage。如果搜索很复杂,加 search index。如果读请求变热,加 cache。
你可以按阶段解释演进。
第一版:
- 一个 API service
- 一个关系型数据库
- 必要时使用 object storage
- 异步任务用 worker
- 基础 logging 和 metrics
第二版:
- 用 queue 提高后台任务可靠性
- 对重复读取加 cache
- 对文本查询加 search index
- 对昂贵 endpoint 加 rate limit
- 对失败任务加 retry 和 dead-letter queue
第三版:
- read replica
- sharding 或 partitioning
- 多区域策略
- 更完整的 observability
- 成本控制
这种分阶段答案很强,因为它展示判断力。你没有忽略 scale,只是把 scale 放到正确的顺序里。
6. 不要表演 scale
New Grad 经常害怕估算规模,但面试官通常不需要你算得非常精确。他们想看的是你是否理解数量级。
用简单估算就可以:
- daily active users
- 每个用户每天读多少次
- 每个用户每天写多少次
- 每条记录大概多大
- 峰值流量是平均值的几倍
比如:
如果有 100,000 daily users,每个人每天查看 tracker 5 次,就是每天 500,000 次读取 session。如果每次加载 50 个 application,那在缓存和分页之前,可能是每天 25 million 行级读取。我会对 application list 做分页,用 userId 和更新时间建索引,并避免每次页面加载都拉完整历史。
这就够了。除非题目本身是存储型系统,否则不需要每个 byte 都算。
关键是把估算连接到决策:
- 高读取量意味着 pagination、cache、index 或 read replica。
- 高写入量意味着 queue、batch、幂等性和清晰事务边界。
- 大文件意味着 object storage 和 metadata table。
- 全球低延迟意味着 CDN、edge cache 或区域部署。
- 强正确性需求意味着 transaction 和一致性比最终一致性更重要。
Scale 讨论不应该是一段仪式,它应该改变你的设计。
7. 失败场景:让你听起来像真正做过系统的人
失败场景能把课堂答案和生产意识区分开。能说出哪里会坏的候选人,会显得更可靠。
大多数早期系统设计题可以讨论这些失败:
- 数据库不可用
- 外部 provider 失败
- 用户重复点击导致重复请求
- background job 很慢
- 部分操作完成、部分失败
- 用户输入无效
- 权限错误
- cache 过期或 stale
- queue backlog
- 数据删除或隐私请求
然后解释一两个缓解方式。
比如提醒系统:
如果 email provider 暂时不可用,reminder worker 应该用 exponential backoff retry,并把 reminder 标记为 pending,而不是直接丢失。如果同一个 job 被重复处理,发送操作应该具备幂等性,用唯一 reminder ID 避免用户收到重复邮件。
这听起来很实用。
文件上传系统:
如果 metadata 已经保存但文件上传失败,系统应该把文件标记为 incomplete,之后清理;或者设计成客户端先上传文件,storage 确认成功后 API 再创建最终记录。
这听起来像处理过真实边界情况的人。
8. 面试表达框架
你可以按这个顺序回答:
- 澄清需求。
- 定义核心实体。
- 设计 API。
- 画出架构。
- 走一遍主用户流程。
- 讨论数据存储和索引。
- 讨论规模和瓶颈。
- 讨论失败场景。
- 总结取舍。
最重要的是 walkthrough。画完框图后,用一个用户动作从头到尾讲一遍。
例子:
当用户保存一个职位时,客户端把职位数据发给 API。API 验证 session,标准化 official URL,检查这个用户是否已经保存过同一个 URL,然后写入 application row。响应返回 application ID。如果用户添加 reminder,系统写入 reminder record,之后由 background worker 发送邮件。Tracker 页面按 user ID 和 status 读取 applications,并按更新时间分页。
这能把产品、API、数据库和 worker 连成一个清楚故事。
9. 适合早期工程师的练习题
练习题最好贴近早期工程师可能真正参与的系统。
推荐练习:
- 设计一个求职 application tracker。
- 设计一个通知偏好系统。
- 设计一个文件上传和分享流程。
- 设计一个短链接系统。
- 设计一个评论系统。
- 设计一个 feature flag 服务。
- 设计一个简单 analytics dashboard。
- 设计一个简历版本历史系统。
- 设计一个预约 tutoring 的系统。
- 设计一个餐厅 waitlist 系统。
不要只练巨型 consumer app。大题有用,但小题更能训练清晰度,也更贴近项目、实习和 junior team feature。
10. 如何把项目变成系统设计证明
RoleProof 用户不应该把系统设计和简历分开看。你最强的项目可以变成系统设计证明,只要它展示产品流程、数据模型、API 决策、取舍和结果。
弱项目 bullet:
Built a full-stack job tracker with Next.js and PostgreSQL.
更强的证明:
Built a job tracker with resume-version history, application status events, and 追问 reminders; designed the PostgreSQL schema aroundApplication,ResumeProfile, andApplicationEventrecords so users could review conversion patterns across saved, applied, rejected, interview, and offer states.
这条更长,但它证明了系统思维。它写出了产品问题、数据模型和用户结果。
一个项目能成为系统设计证明,通常要回答:
- 你支持了什么用户流程?
- 你设计了什么 API 或状态转换?
- 哪些表或实体最重要?
- 哪里可能出错?
- 你做了什么取舍?
- 如果做第二版,你会改什么?
如果你的简历只写“built app”,面试官必须自己推断这些东西。不要让他们推断。把证明写出来。
11. 14 天练习计划
- 第 1 天选 3 个和目标岗位匹配的系统设计题。
- 第 2 天给每个题写需求和 out-of-scope。
- 第 3 天给第一个题设计 API。
- 第 4 天给第一个题设计数据模型。
- 第 5 天大声讲一遍主用户流程。
- 第 6 天加上 scale 假设和索引。
- 第 7 天加上失败场景和 retry。
- 第 8 天重复第二个题。
- 第 9 天录音,用 20 分钟讲第二个题。
- 第 10 天用评分表给自己的解释打分。
- 第 11 天重复第三个题。
- 第 12 天重写一条项目 bullet,让它体现系统设计证明。
- 第 13 天做一次 mock interview,中途加入一个约束变化。
- 第 14 天整理一页系统设计 cheat sheet,包括回答框架、常见组件和失败场景清单。
12. 常见错误
- 从工具开始,而不是从需求开始。
- 把听过的组件全塞进去。
- 跳过数据模型。
- 画了架构,却没有走一遍用户流程。
- 把 scale 当成 buzzword 段落,而不是用它支撑技术决策。
- 忽略失败场景。
- 听起来过度确定。好的工程师会说第一版怎么做、会监控什么、什么信号会让自己改变设计。
13. RoleProof 下一步动作
读完这篇攻略后:
- 对你的目标 SDE 岗位跑一次 Resume Diagnosis。
- 用 Project Repair 修一个最适合变成系统设计证明的项目。
- 重写一条项目 bullet,围绕 API、数据模型、取舍和结果。
- 保存 10 个官方来源 SDE 职位,对比里面反复出现的 backend/system 要求。
- 用上面的评分表练一个早期工程师系统设计题。
目标不是背架构,而是让你的工程判断变得可见。
FAQ
New Grad 软件工程师需要准备系统设计吗?
有些 New Grad 岗位不会考完整系统设计,但很多早期面试仍会考产品推理、API 设计、数据建模和技术取舍。准备一个轻量框架,即使面试不叫 system design,也会有帮助。
系统设计面试里应该主动说 microservices 吗?
不应该默认说。先从简单服务边界开始,只有当需求真正需要独立扩展、独立负责范围、独立部署或更强可靠性时,再拆服务。
New Grad 最适合先练什么系统设计题?
求职 tracker、通知系统、文件上传流程、评论系统、短链接系统,都比一上来练巨型 app 更适合。小题更容易训练需求、API、数据模型和失败场景。
这和我的简历有什么关系?
你的最强项目可以变成系统设计证明,只要简历能展示用户流程、API 决策、数据模型、取舍、验证和结果。RoleProof 的 Project Repair 就是为这种转换设计的。
可以直接练的具体例子
把这一段当成练习,不要照抄。对于把早期工程师系统设计讲成可信工程判断,真正有价值的不是更漂亮的措辞,而是这些细节里的证明:API 边界、数据模型、异步提醒 worker、失败恢复。如果面试官连续追问两次,同一组事实仍然应该能支撑你的回答。
例子 1:通知偏好和求职 tracker 状态
弱回答只会说自己做过这个事情,然后停在那里。它没有说明对象是什么、约束是什么、你做了什么判断,也没有说明为什么这段经历值得招聘者相信。
更强的版本会先交代场景,再写清你负责的对象,说明你做出的选择,最后用API 边界、数据模型、异步提醒 worker、失败恢复支撑结果。重点不是把经历吹大,而是让经历变得可检查。
例子 2:把混乱经历整理成 proof
先收集原始事实:谁需要这件事,哪里不清楚或出了问题,你手上有什么数据或材料,你亲自改变了什么,之后发生了什么。然后删掉所有你在面试里解释不了的句子。
面试可用的 proof 通常很具体:它有用户或 stakeholder,有工作对象,有判断过程,有结果信号,也有仍然存在的限制。这个组合比一句漂亮但空泛的说法更难伪造,也更可信。
7 天升级计划
- 第 1 天:收集和通知偏好和求职 tracker 状态相关的原始事实、截图、记录、指标、例子或证据材料。
- 第 2 天:用一句话写清问题,并定义谁会在意这个结果。
- 第 3 天:列出具体对象:文件、表、dashboard、工单、客户、患者、campaign、账户或流程。
- 第 4 天:写出判断路径,包括你考虑过什么、放弃了什么、为什么这样选。
- 第 5 天:补上证据:API 边界、数据模型、异步提醒 worker、失败恢复。如果没有数字,就用复盘记录、前后状态、演示路径或复盘记录。
- 第 6 天:准备 3 个面试官可能追问的问题,并在不新增虚假说法的情况下回答。
- 第 7 天:重写简历 bullet、作品集段落或面试故事,让它更短、更清楚、更容易验证。
低于招聘标准的常见错误
- 所有岗位都套同一个框架,却没有说清真实工作对象。
- 先加高级词,再找证据,导致内容听起来空。
- 写了无法解释、无法测量、无法被证据材料支撑的结果。
- 跳过取舍,让经历听起来像没有难度。
- 没有下一步:如果再给一周,你会改进、监控、测试或澄清什么。
系统设计诊断:通知偏好和求职 tracker 状态
系统设计面试真正考的不是图画得多复杂,而是你能不能把用户动作、数据归属、可靠性和成本放在同一个判断里。对于把早期工程师系统设计讲成可信工程判断,可以把通知偏好和求职 tracker 状态当成准备锚点,并反复回到API 边界、数据模型、异步提醒 worker、失败恢复。你的目标是留下清楚的准备线索:该收集什么工作对象、要解释什么判断、哪些证据需要经得住追问。
在润色之前,先准备需求笔记、接口草图、数据模型、流程图和失败场景清单。如果其中一块缺失,先不要急着把句子写漂亮;更好的做法是补事实,或者把说法缩小到真实可解释的范围。
定稿前先做四件事
- 写清这段系统设计回答要回答的问题。
- 说出具体对象:表、流程、账户、患者场景、功能、模型、campaign、工单或项目页面。
- 把你个人做的动作,和团队、课程、公司共同完成的结果分开。
- 补一个结果信号:指标变化、复盘记录、交付痕迹、质量改善、客户反馈或学习结论。
弱稿到强稿:改写示范
下面的写法只提供结构,最终要换成你的真实事实。强稿不是更夸张,而是更窄、更清楚、更能解释。
弱稿:“我设计了通知偏好和求职 tracker 状态,并考虑了扩展性。”
强稿:“我先从通知偏好和求职 tracker 状态里的用户动作开始,定义 API 边界,再画出数据模型,最后说明哪个失败场景会改变这个设计。”
强稿更可信,是因为它给面试官留下了可以检查的材料:API 边界、数据模型、异步提醒 worker、失败恢复。同时它没有把结果说满,保留了限制,反而更像真实工作。
这个岗位专用的评分视角
| 视角 | 强信号 | 修复动作 |
|---|---|---|
| 需求清晰 | 回答能说清用户动作、成功条件和非目标。 | 把开场改成一个具体用户动作。 |
| 接口和数据 | 接口形状和数据归属是连在一起讲的。 | 补一个请求/响应或表结构草图。 |
| 取舍 | 能解释为什么没有选择另一个合理方案。 | 写出被放弃的方案和成本。 |
| 可靠性 | 失败、重试、幂等或降级行为可见。 | 补上最先会坏的地方和恢复方式。 |
| 表达 | 最后能用 30 秒复述整个设计。 | 收尾时压缩成一段短总结。 |
早期工程师演练:求职 tracker 提醒
选一个小系统,把它讲完整。比如求职 tracker 的提醒功能,第一版范围可以是:保存申请记录、绑定简历版本、设置后续提醒、修改申请状态,到期后发送一封邮件。团队协作、复杂分析和多区域投递先放到 out of scope。
| 层次 | 具体准备细节 | 可能追问 |
|---|---|---|
| API | POST /applications、PATCH /applications/:id/status、POST /applications/:id/reminders、GET /applications?status=applied | 如何校验用户归属,如何避免重复保存同一个申请? |
| 表结构 | Application、ResumeVersion、Reminder、ApplicationEvent | 为什么要保存 event,而不是只保存最新状态? |
| 后台任务 | 定时 worker 找到到期 reminder,发送邮件,并写入 sent、failed 或 retry_at。 | 如果 worker 发完邮件但还没更新数据库就崩了怎么办? |
| 索引 | (user_id, status) 支撑列表页,(due_at, state) 支撑提醒 pickup。 | 哪个查询会最先变慢? |
| 失败处理 | 提醒发送要幂等,失败要 backoff 重试,并把提醒状态展示给用户。 | 如何避免同一条提醒发两次? |
这对 New Grad 已经够强,因为它讲完了一整个闭环:产品范围、API、数据模型、后台任务、索引、失败处理,以及每个组件为什么存在。除非面试官给出明确规模约束,否则不需要主动塞 Kafka、Kubernetes 或 sharding。
只针对这篇攻略的练习题
- 不用夸张词,在 45 秒内讲清通知偏好和求职 tracker 状态。
- 定义最重要的证据:API 边界、数据模型、异步提醒 worker。
- 说明面试官或招聘者可以在哪里检查这段经历。
- 写出一个限制,让说法保持真实。
- 围绕 API 边界重写一条简历 bullet、作品集说明或面试回答。
- 回答最难追问:“你怎么知道这个解释是对的?”
- 如果这是真实工作,下周你会先做什么。
- 删掉一句听起来厉害但解释不了的话。