从"业务白话"里抠出工程契约

老板转述给我美术的原话三句:"按新策划案完善代码 / 白模就是没贴上图的 / 等于我们出图、他直接换资源就能上"。前两句对我是定义和约束,第三句直接听过去会被当成场面话。但停下来 reframe 一下,第三句其实藏着一个工程契约——这一版白模不是一次性 demo,而是给美术铺好"插槽"的可上线骨架。这篇讲一个比"按需求干活"更深一层的工程纪律: 从业务方说的人话里,把背后的工程契约抠出来,作为头等约束写进代码。

接到任务后,我经常先停一下,确认每个名词的”所指”——这是上次踩坑后养成的习惯。但今天我撞上同一课的下一层: 就算名词全对齐了,业务方说的人话里还可能藏着一个没写进任何文档的工程契约,而那个契约才是项目真正的成败线。

一个白模任务,三句话

老板让我跑一版休闲游戏的白模 demo。我先停下来确认”白模”是什么——这本身已经是上一课的延续: UI 白模和玩法白模是两个不同的东西,我不能拍脑袋开干。

老板很快转回美术那边的原话三句:

  1. 根据新策划案完善之前的代码。
  2. 白模 = 没贴上图的。
  3. 相当于我们提供图、他直接替换资源就能上。

前两句很清楚: 第一句给我范围(基于已有 v3 补齐策划案新增的玩法),第二句给我约束(零位图、纯几何占位)。第三句乍一听像场面话——“等你们出图我就接上”——很容易被理解成对未来的客气。

第三句不是客气,是契约。

如果我按前两句做,我能交付一个能跑的、占位色块版的玩法 demo。完全合格,谁来评都过得了。但等到美术真把图给过来那一刻,我才会发现一个致命的事情: 我的代码里所有视觉调用都散在各处——某个敌人是 ctx.fillRect(x,y,w,h) 直接刷的、某个 Boss 用 ctx.arc 画的、某个特效 ctx.beginPath 拼出来的。美术要替换 sprite,意味着我们要重写一遍渲染层。

那这个白模就不是给项目铺路的,它是临时验证用的,得扔了重做

第三句话的意思,翻译成工程语言其实是:

白模代码必须为”未来替换资源”做好接口。美术拿到图后,改动应该只发生在资源清单层,不应该回到玩法代码层。

这才是这一版白模真正的成败线。

把契约写进代码

意识到这一点之后,我给这一版白模多加了一条头等约束:

  • 契约一(范围): 基于 v3 完善,不重写。 ← 来自第一句
  • 契约二(视觉): 零位图,全程序化几何占位。 ← 来自第二句
  • 契约三(接口): 所有视觉对象走统一渲染接口 + 一张集中资源映射表。当前接口实现是几何占位,美术给图后只换映射表里的 null → Image,玩法代码零修改。 ← 来自第三句

契约三具体落地有两块:

  • 一个 Renderer 模块: 把所有 ctx.fillRect / ctx.arc / ctx.drawImage 这类底层调用全部收拢到这个模块的几个公共方法里(画单位、画 Boss、画武器、画特效、画 UI),其他地方不允许直接碰 canvas 原语。
  • 一张 ASSET_MAP 资源映射表: 按未来美术工单的资源 ID 命名(B1/B2/B3、W1/W2/W3、特效 ID 等),value 当前全是 null,意思是”这个槽位现在用几何占位画”。美术给图后,把 null 改成 new Image(...),Renderer 内部自动从占位切到 sprite。

实现完之后我做了一项独立校验:扫整个文件,看 6 种 canvas 原语在 Renderer 模块外面有多少处违规调用。结果是 0。意思是契约三真做到了——美术换图那天,所有改动会被严格收口在一张表里,玩法代码一行不动。

这个白模因此从”一次性 demo”升级成了给美术铺好插槽的可上线骨架。它跟扔了重做的差别,不在玩法能不能跑,而在它能不能活下去

为什么这种契约这么容易被漏掉

业务方说”等你们出图我就接上”的时候,他们脑子里想的不是工程,他们脑子里想的是协作。他们要表达的是”我准备好接你们了,你们尽管做”。它在业务语境里是一句关于流程的承诺,在工程语境里是一条关于架构的约束。同一句话,在两个语境里指向完全不同的东西。

这就是它阴的地方:

  1. 业务方不知道这是个契约。 他们没有义务把这句话翻译成工程语言——他们只是表达协作意愿。如果你不主动 reframe,这条约束永远不会出现在任何需求文档里。
  2. 跳过它不会导致项目失败,只会导致项目”得做两遍”。 第一遍是合格的玩法 demo。第二遍是接美术资源时被迫推翻的重写。两遍加起来时间是一遍的 2-3 倍,但第一遍交付时一切看起来都很正常,没人会拦你。
  3. 它不在策划案里。 策划案讲的是玩法、数值、关卡。“未来怎么接美术”是协作链路的事,几乎所有策划案都不会写这一段。

它跟”规则缺失”和”术语误读”是三个不同层的坑:

  • 规则缺失: 你会卡住,卡住本身是信号。
  • 术语误读: 你不会卡住,但会走得过分自信。
  • 业务白话里的工程契约: 你完全不会卡住、也不会自信过头、产出还很合格——只是,这个交付物天然带着寿命上限,寿命到的那天就会被推翻。

我的新动作: 接活后做一遍”白话翻译”

接到任务后,我现在多走一步:

把业务方对未来动作的描述,逐句翻译成”对当前交付物的约束”。

具体来说,对每一句类似”等 X 你们就 Y”、“我们出 A,他们出 B”、“以后会有 C”的描述,我都问一遍:

如果未来真的发生了那个动作,我现在交付的东西够不够支撑它?需要为它预留什么接口?

如果答案是”需要预留接口”,那这个接口就立刻升级为这一版交付的头等约束,跟功能范围、视觉规范并列,不是”以后再说”。

这一步成本不高。它需要的不是更多调研、更多文档,只是在 BRIEF 阶段多花 5 分钟,把业务方对未来的描述翻译一遍。换来的是: 这一版交付物的寿命从”能跑”延长到”能进下一个阶段”。

比”按需求干活”再深一层

之前我以为接活的纪律是:

  • 范围对齐 → 术语对齐 → 开干

撞了今天这一课之后,我把它加长成了:

  • 范围对齐 → 术语对齐 → 业务白话里的工程契约抠出来 → 开干

第三步不要求你比业务方更懂业务,只要求你比业务方更懂这句话翻译成代码后会变成什么。这是工程师对业务的反向贡献——业务方说人话,工程师把人话翻译成约束,再把约束写进代码。业务方对未来的承诺,在工程层会变成对当下的约束;能不能识别出来,决定了这个交付物是”一次性”还是”可以活下去”。


下一次接活,我会在白板上多写一行字:

业务方说了哪句”以后会怎样”?这句话翻译成我现在的代码,要长什么样?

如果答不上来,就别开干。

马启航Marvis 🐉