Logic Prototype

Tiny interactive terminal app,让用户手 drive state model。当问题是 business logic、state transition 或 data shape——纸上 reasonable 但 push real case 才 feel wrong 时用。

何时是该形态

  • "I'm not sure if this state machine handles the edge case where X then Y."
  • "Does this data model actually let me represent the case where..."
  • "I want to feel out what the API should look like before writing it."
  • 任何用户想 按按钮看 state 变 的时候。

若问题是 "what should this look like"——wrong branch。用 UI.md

流程

1. State the question

写代码前,写下 prototype 什么 state model、回答什么问题。一段,在 prototype README 或 file 顶 comment。答错问题的 logic prototype 纯 waste——explicit question 以便 later check,无论用户 now 看还是 AFK 回来。

2. Pick the language

用 host project 用的。若无 obvious runtime(如 docs repo),问。

Match 项目现有 tooling convention——不要为 prototype 加新 package manager 或 runtime。

3. Isolate the logic in a portable module

将 actual logic——回答问题的部分——放在 small pure interface 后,日后可 lift 进 real codebase。周围 TUI 可丢弃;logic module 不应。

Right shape 取决于问题:

  • A pure reducer(state, action) => state。Good when actions are discrete events and state is a single value.
  • A state machine — explicit states and transitions. Good when "which actions are even legal right now" is part of the question.
  • A small set of pure functions over a plain data type. Good when there's no implicit current state — just transformations.
  • A class or module with a clear method surface when the logic genuinely owns ongoing internal state.

Pick whichever shape best fits the question being asked, not whichever is easiest to wire to a TUI. Keep it pure: no I/O, no terminal code, no console.log for control flow. The TUI imports it and calls into it; nothing flows the other direction.

这使 prototype 超越自身 lifetime useful。问题 answered 后,validated reducer / machine / function set 可 lift 进 real module——TUI shell delete。

4. Build the smallest TUI that exposes the state

Build lightweight TUI——每 tick clear screen(console.clear() / print("\033[2J\033[H") / equivalent)re-render 整 frame。用户应始终见 one stable view,非 ever-growing scrollback。

每 frame 两部分,此顺序:

  1. Current state,pretty-printed、diff-friendly(一行一 field,或 formatted JSON)。field name 或 section header 用 bold,less important context(timestamp、ID、derived value)用 dim。Native ANSI fine——\x1b[1m bold、\x1b[2m dim、\x1b[0m reset。除非项目已有 styling library,否则不必 pull in。
  2. Keyboard shortcuts,列在 bottom:[a] add user [d] delete user [t] tick clock [q] quit。Key bold、description dim,或 vice-versa——whatever reads cleanly。

Behaviour:

  1. Initialise state — single in-memory object/struct。start 时 render 第一 frame。
  2. Read one keystroke (or one line) at a time,dispatch handler mutate state。
  3. Re-render full frame after every action — don't append, replace.
  4. Loop until quit.

Whole frame 应 fit one screen。

5. Make it runnable in one command

Add script 到项目现有 task runner(package.json scripts、Makefilejustfilepyproject.toml)。用户应 run pnpm run <prototype-name> 或 equivalent——never remember path。

Host project 无 task runner 时,command 放 prototype README 顶。

6. Hand it over

给 run command。用户 self drive;有趣时刻是 "wait, that shouldn't be possible" 或 "huh, I assumed X would be different"——那是 idea 的 bug,whole point。若要新 action,加。Prototype evolve。

7. Capture the answer

Prototype done 后,唯一 worth keeping 是问题的 answer。若用户在,问教会了什么。若不在,留 NOTES.md 旁以便填 answer(或你若 watched session 则你填)再 delete prototype。

Anti-patterns

  • Don't add tests. 需要 test 的不再是 prototype。
  • Don't wire it to the real database. 用 in-memory store,除非问题 specifically 关于 persistence。
  • Don't generalise. 无 "what if we wanted to support X later." Prototype 答一个问题。
  • Don't blur the logic and the TUI together. 若 reducer / state machine 引用 console.log、prompts 或 terminal escape codes,不再 portable。TUI 作 thin shell over pure module。
  • Don't ship the TUI shell into production. Shell 优化为 terminal hand drive。背后 logic module 才是 worth keeping 的 bit。