Skip to content
Published on

FDE 打法手册:用「可丢弃原型」取胜

分享
Authors

引言 — 不是幻灯片,而是能跑起来的东西

在前向部署工程师(FDE)的世界里,最强大的武器不是做得精致的演示文稿,而是能在客户真实数据上实际跑起来的原型。比起一百页的提案书,让客户亲眼看到自己的数据在屏幕上动起来的那三分钟,说服力要大得多。

而这类原型有一个决定性的特征。从一开始就是为了被丢弃而做的。 不像生产代码那样精雕细琢,而是只为了证明价值而快速拼装,一旦证明完成,就毫不留恋地丢掉。这篇文章就是关于如何靠这种「可丢弃原型」取胜的打法手册。

为什么要用可丢弃原型

按传统的工程直觉来看,这种做法显得很浪费——反正要丢,为什么还要做?答案很明确。原型的目的不是留下代码,而是留下学习和确信。

想想几天的原型能回答哪些问题。

  • 这项技术对这个问题真的管用吗?
  • 客户的数据真的像我们假设的那样干净,还是一团糟?
  • 客户真正想要的,是不是我们想象中的那个东西?
  • 朝这个方向投入六个月,值得吗?

这些问题的答案,不管把文档读多少遍都不会出现。只有真正动手做出来才会出现。而一旦拿到答案,那段粗糙的代码本身就已经完成了它的使命。

Spike:先把风险排除掉

敏捷的世界里有一个叫 spike 的概念——一种限定时间、优先戳向不确定性最大之处的探索性工作。FDE 的原型,本质上就是一个巨大的 spike。

核心在于最先验证风险最大的假设。如果这个项目会失败,会在哪里失败?先攻克那个点。

比如说,「从客户的十万份 PDF 合同里提取特定条款」这样的项目,最大的风险既不是 UI 也不是认证,而是模型能否真正从那些杂乱的真实 PDF 里正确提取出条款。所以 UI 放到后面,先在几天之内,用真实的几百份 PDF 跑一遍模型,确认准确率。如果这一步做不到,剩下的就都没有意义。

Spike 的规则很简单。

  • 限定时间:「两天内得出结论。」spike 不能无限期拖下去。
  • 明确问题:提出一个「行还是不行」的是非问题。
  • 产出是知识:成功换来信心,失败换来转向。两者都是收获。

刻意为之的技术债:可以借,但要记录

在可丢弃原型里,技术债不是罪过,而是策略——前提是它必须是刻意的

「坏债」是在不知不觉中堆积起来的债。「好债」是睁着眼睛借下的债——「现在速度更重要,所以在这里抄近路;等到了生产环境,再这样正正经经地把它做好」,自己心里清楚地借下这笔债。

处理刻意技术债的原则是这样的。

  • 记录下这是债:在抄近路的地方留下标记。展示原型时,也要坦率地说明「这部分是给演示用的临时处理」。
  • 把核心逻辑和临时处理分开:真正证明价值的核心部分(比如提取逻辑)要认真对待,周边部分(认证、存储、UI)则大胆地从简。
  • 保持可回退:清楚地留下记录,标明日后要拆掉这段近路时该动哪里。

什么时候该硬编码

在原型里,硬编码不是丢人的事,而是一项强大的技巧。问题在于「硬编码在哪里」。

原则是这样的:凡是在你要证明的价值之外的东西,都是硬编码的候选。

  • 认证/权限:在演示里,登录流程通常不是价值所在,直接塞进一个 token 就够了。
  • 配置值:客户名称、阈值、类别列表之类的,先硬塞进代码里,以后再抽成配置。
  • 周边集成:还没接上的外部系统,用假响应来模拟。

反过来,有一样东西是绝对不能硬编码的——那就是演示的心脏,也就是你要证明的那个价值本身。如果在条款提取的演示里,把提取结果提前手工填好,那就不是演示,而是骗局。只有当客户当场把自己的文档扔进来、系统也真的能跑出来的时候,那个瞬间才是 aha 时刻。

下面是把这种感觉写成代码的例子。真正的价值(提取)是实际运行的,其余的部分则堂而皇之地硬编码。

# demo_extractor.py — 演示用原型(会被丢弃的代码)

# [可以硬编码] 不属于演示价值的部分
DEMO_CUSTOMER = "acme-corp"
FAKE_AUTH_TOKEN = "demo-token-do-not-ship"
TARGET_CLAUSE_TYPES = ["indemnification", "termination", "liability"]

def get_current_user():
    # [刻意的技术债] 演示中只是模拟认证。生产环境中要换成真正的 OAuth。
    return {"org": DEMO_CUSTOMER, "token": FAKE_AUTH_TOKEN}

def extract_clauses(pdf_text: str) -> list[dict]:
    # [真正的价值] 只有这部分必须真正能跑。绝对禁止硬编码。
    # 对客户当场扔进来的文档也能真正跑起来,才是演示的核心。
    prompt = build_extraction_prompt(pdf_text, TARGET_CLAUSE_TYPES)
    response = call_llm(prompt)
    return parse_clauses(response)

def save_results(results: list[dict]) -> None:
    # [刻意的技术债] 演示里只是写本地 JSON。生产环境中要换成数据库。
    import json, pathlib
    pathlib.Path("demo_output.json").write_text(json.dumps(results, indent=2))

以最短路径抵达 aha 时刻

FDE 原型开发的目标最终收敛成一件事:用尽可能快的速度,把客户带到 aha 时刻。 所谓 aha 时刻,是指客户不是用脑子、而是用眼睛理解到「啊,原来这样就能解决我们的问题」的那一瞬间。

缩短这段距离,有几条原则。

  • 用客户的数据来演示:不管用我们自己的样例数据跑得多好,客户听到的都是别人的故事。只有当客户自己的数据动起来的那一刻,这件事才真正变成他们自己的事。
  • 只打磨出一条最有冲击力的流程:与其把十个功能都做得半生不熟地展示出来,不如把一条「哇」的流程打磨得顺畅完整。演示比的是深度,不是广度。
  • 在前 30 秒定胜负:客户的注意力很短暂。不要把时间耗在登录、设置、引导上,一打开就要展示核心价值。
  • 展示真实的结果:不能是模型图,而必须是真正计算出来的结果。客户能一眼分辨真假。

期望管理:坦率地说这是演示

可丢弃原型最大的风险不是技术问题,而是误解。如果演示做得太顺畅,客户(有时甚至包括自家的销售)会觉得「基本都做完了吧,下周就能上线了吧?」——尽管一个几天做出来的演示和生产环境之间,其实隔着好几个月的距离。

所以期望管理是原型开发中必不可少的另一半。

  • 明确说明这是演示:提前说清楚「这是为了展示价值的原型,背后有大量临时处理」。
  • 展示到生产环境为止还剩下的工作:把演示背后需要的真正工作(错误处理、规模扩展、安全、集成)列成清单一起展示出来。
  • 用「冰山」的比喻:说明演示只是露出水面的冰山一角,生产环境才是水面之下庞大的主体。

把这件事做到位,就能既保住演示带来的震撼,又避开不切实际的排期压力。

从原型到生产

演示成功、合同也在推进之后,真正的问题就来了:该怎么把这段原型代码变成生产代码?

这里常见的错误有两个。一个是把原型原封不动地塞进生产环境(临时处理原样留下,变成一颗炸弹);另一个是从头把一切都重写一遍(把学到的东西全部丢掉)。好的答案通常介于两者之间。

  • 保留核心逻辑,更换外壳:原型里真正被验证过的,是核心价值逻辑(提取、匹配、计算)。这部分要好好打磨并保留下来;而硬编码过的认证、存储、UI、集成,则要重新正正经经地实现。
  • 把刻意留下的技术债一笔一笔还清:以之前留下的债务标记为地图,把临时处理逐一替换成正式实现。
  • 把原型当作规格说明书来用:原型本身就是「客户想要什么」最准确的说明书。即便要重写,也要以这份活生生的规格为基准。
  • 重新划定边界:这时候要判断,哪些是这一个客户专属的定制,哪些应该做成产品里的通用功能。(这个话题会在下一篇文章里深入讨论。)

反模式:出现这些信号就该停下

可丢弃原型如果用错了,也会变成毒药。出现以下信号时,应该重新审视自己的做法。

  • 演示拖了好几个星期:本该只要几天的原型如果拖成了几个星期,那它就已经不再是原型,而是没有方向的生产开发了。
  • 对要丢弃的代码产生了感情:如果冒出「这段写得挺好,丢了可惜」的念头,那是危险信号。原型只有在能被丢弃的时候才是自由的。
  • 只在假数据上跑得好:如果在客户的真实数据上就崩掉,说明风险还没有被真正验证过。
  • 没有人感受到 aha 时刻:如果客户看完演示反应平淡,那问题本身或者价值假设可能就是错的。

结语

FDE 的原型开发不是「随便做做」的技巧,而是「精确知道该在哪里随便做做」的技巧。先用 spike 戳破风险最大的假设,在价值之外的部分堂而皇之地硬编码,以最短路径把客户带到属于他们自己数据的 aha 时刻,诚实地把演示叫作演示,只把经过验证的核心部分提升到生产环境。

可丢弃原型的悖论在于:正因为一开始就做的是要丢弃的东西,最终反而留下了最有价值的东西——确信、方向,以及客户的信任。下一篇文章会讲述,这样为一个客户做出来的东西,是如何成长为服务所有客户的平台的。

参考资料