练习以生产思维为导向的 FDE 编程题目,边做边沟通,应对不断变化的需求,通过技术面试的门槛。
由 The Forward Deployed 编辑团队撰写并审阅
这里记录的每一个 FDE 面试流程都以编程关卡开场,之后才会对四个信号中的任何一个进行评分。通过了,流程才正式开始;没通过,你准备的其他一切都不会被看到。纵观这些记录在案的筛选环节,模式是一致的:题目偏实用而非算法,题目包裹在用户的场景中,限时完成,而且需求会在中途扩展。这些说法的依据来源都在 Interview Method 页面上;本页则把它变成练习。
请合理分配时间。方法页的建议依然成立:在这里做维持水平的练习,把大部分时间花在四个信号上。下面就是这些维持性练习应有的样子。
失败模式与拆解环节如出一辙:大多数候选人跳过了第一步,要么设计了一个时间预算根本撑不起的系统,要么闷头苦干、默默做出一个没人看到过程的完整答案。
练习——一次完整运行的筛选环节
题目:“某客户的客服团队将其工单队列导出为文本文件:每行一条工单,每条包含 ID、客服姓名、状态和一个创建时间戳,分隔符不统一。请编写一个程序,打印每位客服持有的未关闭工单数量。你有二十五分钟。”
扩展需求,大约在中途给出:“另外打印每位客服最早的一条未关闭工单,并将任何缺失状态的工单视为未关闭。”
在继续往下读之前:设一个计时器,用任何语言、大声说出你的思路来试做。然后对照下面的阶梯给你的前十分钟打分。
差 / 好 / 优秀——前十分钟
差——还没跑起来就先做架构,或者全程沉默。同一失败的两种版本。第一种,到了第十分钟,你有一个 Ticket 类、一个分隔符策略抽象,以及一个为未来导出格式准备的插件系统草图,但没有任何输出,因为什么都还没执行。你在为没人提出的需求做设计,而已提出的需求却还没满足。第二种版本,你写出了还不错的可运行代码,却一个字都没说出来;面试官只看到有人在打字。无论哪种,都没有留下这一环节所考察内容的可评分证据:一个能在限时下交付、并在此过程中展示推理过程的人。
Ticket
class, a delimiter-strategy abstraction, and a sketch of a plugin system for future export formats, and no output, because nothing executes yet. You designed for requirements nobody stated while the stated one sits unmet. In the second version you have decent working code and have said none of it aloud; the interviewer has watched someone type. Either way there is no graded evidence of the thing the round measures: a person who ships under a clock and shows their reasoning while doing it.
很好——快速写出能运行的代码,但融合在一起。几分钟后你就有了真实结果:按逗号拆分每一行,保留状态为 open 的行,按 agent 统计到映射中,然后打印。输出正确,而且很早:这确实是一个强劲的开局,比大多数第一次尝试都好。但解析、过滤和报告都挤在一个循环里,而逗号是承重结构。当扩展需求到来时,“最旧工单”的提问和“缺失状态”的规则都属于你的代码从未分离的阶段,于是循环不得不在计时压力下被重写,而你之前略过的“分隔符不一致”那句话,会以面试官指出的 bug 形式卷土重来。
卓越——最简单的可运行版本,边做边讲解,并且为第二个需求做好了准备。你在敲代码之前先说出计划:一个小函数把原始行转换成简单记录,并负责处理所有分隔符的混乱;记录收集到一个列表中;报告读取这个列表。几分钟内完成第一个里程碑:文件输入,输出每个 agent 的计数,并用几行粘贴的数据进行核对。当扩展需求到来时,“最旧工单”的提问只是报告步骤中的几行代码,“缺失状态”的规则只是解析步骤中的一行;没有任何东西被重写,而且你在做的时候也说明了这一点。卓越之所以胜过良好,是因为良好把一切都押在“第一个需求就是唯一需求”上,而卓越付出了很小的早期成本——保持解析和报告分离——这使得第二个需求变得廉价,并且在下注的同时讲出了这个赌注。
预期会遇到的追问
面试官:“这段代码非常朴素。你会就这样上线吗?”
你:“就本次会话而言,会。计时换来了能运行的代码和一个能扛住下一个需求的形状,它做到了。如果这段代码要交付给客户,在别人提出之前,我会先加固这些地方:目前格式错误的行会被静默跳过,所以我会对它们进行计数并记录日志;解析规则会移到测试之下,因为真实的导出格式会漂移。”
这个追问测试的是朴素是否是一种选择。主动指出生产环境的差距并给出理由,就证明了这一点。
练习套件
一个把评分印在题目下方的练习,读过一遍之后就不再能测试你了。下面的套件才是未经排练的练习的来源:二十个任务,本网站上没有任何答案,以及一个把 AI 聊天变成面试官的协议,计时和扩展需求都包含在内。每次练习都是同样的形状:一个任务,二十五分钟,大声说出来,按照上面框架中的四个动作来评分。
任务库
- 解析一家诊所的预约导出文件,其中日期以多种格式出现,打印每位医生当天的日程,并标记重复预约。
- 合并来自一家公司及其刚收购的公司的两份客户名单,对名字和邮箱仅大小写或标点不同的条目去重。
- 为一个支持收件箱构建限流器,允许每位客户每小时发送固定数量的消息,其余的进入队列并附带排队位置提示。
- 为值班工程师总结 Web 服务器的访问日志:最繁忙的路径、每个路径的失败次数,以及出现频率最高的客户端地址。
- 编写一个小型调度器,将配送司机分配到时间段,需遵守每位司机申报的可用时间以及最长班次时长。
- 给定一份仓库库存文件和一天的订单,打印哪些订单可以完整发货、哪些只能部分发货,以及需要补货的商品。
- 用模糊规则为募捐活动去重捐赠者名单:当姓名在忽略中间名缩写的情况下匹配,且地址在忽略公寓号的情况下匹配时,视为同一人。
- 将一位教师导出的成绩册(列不齐、分数缺失)转换为每位学生的平均分,以及每位学生缺交作业的清单。
- 构建一个会议室预订系统:接受包含开始和结束时间的请求,拒绝时间重叠的请求,并在拒绝时报告下一个空闲时段。
- 解析银行的交易数据流,标记当日取款超过第二个文件中所申报限额的账户。
- 从一个 RSVP 邮件的文本文件中,提取每位宾客的姓名、随行人数和饮食禁忌,整理成餐饮承办商可读取的表格。
- 为翻译团队构建一个轮询式任务分配器,需遵守每位译员的语言对和每日工作量上限。
3. 当我报告的已用分钟数第一次超过十二分钟时,恰好扩展一次需求:增加一个相关的要求,使得一个简单、清晰分离、已经能运行的第一版只需几行代码就能吸收,而一个硬编码或尚未运行的第一版则无法做到——例如一个新字段、第二种分组方式、或一条规则的例外。不要提前扩展,也不要第二次扩展。
4. 要求边做边讲解。我会粘贴代码,并在过程中说明我在做什么。如果我连续两条消息只粘贴代码而不说明它在做什么或为什么这样做,就问:"你在想什么?"并把这次沉默记入评分。除非我明确说"提示",否则在整个过程中不给出任何提示、建议或代码——如果我说了,就给出尽可能小的提示,并记入评分。
- 当我说"完成",或时间耗尽时,对四个动作分别标记 PASS 或 FAIL,并为每一项引用具体时刻:(a) 我是否尽早让一个小的可用东西端到端跑通;(b) 我是否先写了简单版本,使扩展无需重写就能落地;(c) 我是否选择了数据处理——解析、分组、转换——而不是聪明的算法;(d) 我是否在打字的同时进行讲解。严格评分:典型的第一次练习至少会在四个动作中的两个上失败——如果没有一项失败,在表扬之前先重新审视,并且绝不因为我争辩而软化评分。
6. 以恰好一条下次练习需要改进的事项结尾。然后提供下一个任务。
下一项:分解演练。标志性环节,信号最广。
下一项分解演练