Skip to content
AI Product Engineer Wiki

    ↑ ↓ to move between results, Enter to open, Esc to close

    Menu

    Turning a prototype into a spec

    Turn what a prototype taught you into a buildable spec, with findings, decisions, the first eval cases and the open risks before engineering starts.

    Level: Intermediate 9 min read Updated
    On this page

    Why this matters

    A prototype teaches you things fast: which inputs break the prompt, what users actually ask for, which states confused people, what a good output looks like to them. Most of that knowledge lives in someone’s head, a chat thread and a folder of results files. Two weeks later, it’s gone.

    Turning a prototype into a spec is the step that keeps it. It separates what you proved from what you hoped, records the decisions and the reasons behind them, converts prototype inputs into your first eval cases, and names the risks the full build still has to retire. This article walks through each part.

    What the prototype proved

    Start with an honest list of findings, each backed by evidence. A useful discipline is to sort every claim into one of three buckets:

    Bucket Example Evidence
    Proved The model drafts correct replies for how-to tickets 17 of 20 real tickets good in prompt test v3
    Suggested Agents trust drafts that cite articles more 4 of 5 Wizard-of-Oz participants said so; small sample
    Assumed Drafts will cut reply time Not yet measured

    The point isn’t to downplay the prototype. It’s to stop “the demo went well” from being read as “the feature works”. Only the first bucket is safe to build on directly. The other two become open questions in the spec.

    Decisions and their reasons

    Prototypes force decisions: which model, which prompt, what the UX does when the answer isn’t in the sources. Record each one with its reason, so nobody re-litigates it later without new evidence:

    • Decision: drafts only for tickets tagged “how-to”. Because: quality on billing disputes was poor in testing (4 of 10 wrong), and those mistakes are costly.
    • Decision: Claude Opus 5.5 at low effort. Because: same pass rate as medium effort on our 20 cases, faster first token.
    • Decision: no draft when no article matches. Because: drafts without sources were the main reason testers rewrote replies.

    The “because” is what makes this valuable. When someone proposes drafting billing replies next quarter, the record says what evidence would change the decision: a better pass rate on billing cases.

    Seeding the eval set

    The inputs you tested during prototyping are real, varied and already reviewed. They’re the best possible start for a golden dataset. Don’t let them sit in a results folder.

    If your prototype logged reviews as one JSON object per line, as a simple review tool or spreadsheet export can, a short script turns them into eval cases. It keeps the input, what the reviewer said a good output must do, and whether the prototype passed. It removes duplicates and keeps the failures, which are the most valuable cases.

    scripts/seed-evals.ts
    import { readFile, writeFile } from 'node:fs/promises';
    
    /** One reviewed prototype output, as exported from your review sheet. */
    interface Review {
      input: string;
      output: string;
      verdict: 'good' | 'fixable' | 'wrong';
      reason: string;
      promptVersion: string;
    }
    
    /** A first eval case: the input, what a good answer must do, and where it came from. */
    interface EvalCase {
      id: string;
      input: string;
      criteria: string[];
      /** Did the prototype pass? Failing cases are the most valuable: they show what still needs work. */
      prototypeVerdict: Review['verdict'];
      source: string;
    }
    
    const lines = (await readFile('prototype/reviews.jsonl', 'utf8')).split('\n').filter(Boolean);
    const reviews = lines.map((line) => JSON.parse(line) as Review);
    
    // Keep the latest review per input, so repeated rounds don't create duplicate cases.
    const latest = new Map<string, Review>();
    for (const review of reviews) latest.set(review.input.trim(), review);
    
    const cases: EvalCase[] = [...latest.values()].map((review, i) => ({
      id: `seed-${String(i + 1).padStart(3, '0')}`,
      input: review.input,
      // The reviewer's reason becomes a first criterion; refine these by hand before relying on them.
      criteria: review.verdict === 'good' ? ['Matches the quality of the approved prototype output'] : [`Avoids: ${review.reason}`],
      prototypeVerdict: review.verdict,
      source: `prototype ${review.promptVersion}`,
    }));
    
    await writeFile('evals/seed-cases.jsonl', `${cases.map((c) => JSON.stringify(c)).join('\n')}\n`);
    
    const failing = cases.filter((c) => c.prototypeVerdict !== 'good').length;
    console.log(`Wrote ${cases.length} cases (${failing} that the prototype did not get right).`);

    The generated criteria are a starting point. Before relying on the set, read each case and write criteria someone else could apply, as described in rubrics and human grading. Check that no personal data survived from the prototype logs.

    Risks still open

    The last part of the spec update names what the full build must still prove, in order of how likely each is to sink the feature. Each risk gets a way to test it:

    1. Quality on the full distribution of tickets. The prototype saw 20. Test: grow the eval set to 100 real tickets before launch.
    2. Reply time actually drops. Assumed, not measured. Test: A/B test against the current workflow with one team.
    3. Cost at full volume. Estimated from 20 requests. Test: track cost per accepted draft during the staged rollout.

    The spec update

    Put it together in the one-page spec from earlier, updated with what you learned. The full flow:

    From prototype to spec. Each step turns prototype experience into something the build can rely on.
    Show text descriptionHide text description

    A sequence of 5 steps.

    1. Findings : Sorted into proved, suggested and assumed, with evidence
    2. Decisions : Each with its reason and what would change it
    3. Eval seed : Prototype inputs become the first golden cases
    4. Open risks : Ranked, each with a way to test it
    5. Updated spec : One page the team can build from

    A compact way to write the new sections:

    specs/support-reply-drafts.md (additions)
    ## What the prototype proved (prompt test v3, Wizard-of-Oz x5, 14 Oct)
    - Proved: correct drafts for how-to tickets (17/20 good, 0 harmful).
    - Suggested: drafts with cited articles are trusted more (4/5 participants).
    - Assumed: reply time drops (not measured yet).
    
    ## Decisions
    - How-to tickets only. Because: billing disputes 4/10 wrong. Revisit if billing pass rate > 85%.
    - Opus at low effort. Because: same pass rate as medium on 20 cases, faster.
    - No draft without a matching article. Because: unsourced drafts were rewritten most.
    
    ## Evals
    - 20 seed cases in evals/seed-cases.jsonl (5 the prototype failed). Target: 100 before launch.
    
    ## Open risks (ranked)
    1. Quality on all how-to tickets → grow eval set to 100.
    2. Reply time → A/B test with one team.
    3. Cost at volume → track cost per accepted draft during rollout.

    Common mistakes

    • Treating a good demo as proof. Sort findings into proved, suggested and assumed, and only build directly on what’s proved.
    • Recording decisions without reasons. Without the “because”, decisions get re-argued every month.
    • Throwing away prototype inputs. They’re real, reviewed and varied: your best first eval cases.
    • Keeping only the successes. Failing cases show what the build still needs to fix. Keep them in the eval set.
    • Building by momentum. Make an explicit build, iterate or stop decision from the evidence.

    Try it

    Exercise

    About 60 min

    Write the spec update for a prototype

    1. List every finding from your prototype and sort each into proved, suggested or assumed, with evidence.
    2. Write three to five decisions, each with a reason and what would change it.
    3. Turn your prototype inputs into at least 20 seed eval cases, keeping the failures.
    4. Rank the open risks and give each a test. End with an explicit build, iterate or stop decision.
    Show a hint

    If you can’t point to a number, a quote or a log entry for a finding, it isn’t proved. Move it down a bucket.

    Show a worked solution

    A good spec update fits on one page and lets someone who wasn’t there answer:

    • What do we know works, and how do we know?
    • What did we decide, and what evidence would change each decision?
    • Where are the eval cases, and how many failed in the prototype?
    • What’s most likely to sink this, and how will we find out?
    • Are we building, iterating or stopping, and why?

    Check your understanding

    Question 1 of 3: "Agents will reply faster with drafts", with no measurement yet. Which bucket does it belong in?

    Show answer

    C. Assumed

    Without evidence, it is an assumption, and it becomes an open risk with a test in the spec.

    Question 2 of 3: Why record the reason behind each decision?

    Show answer

    B. So the decision is only revisited when new evidence addresses the reason

    The "because" tells everyone what evidence would justify changing the decision.

    Question 3 of 3: Why does the seeding script keep cases the prototype got wrong?

    Show answer

    B. Failing cases show exactly what the full build still needs to fix

    An eval set of only passing cases cannot tell you whether the real feature fixed the known problems.

    Key takeaways

    • Sort prototype findings into proved, suggested and assumed, with evidence.
    • Record decisions with reasons and what would change them.
    • Turn prototype inputs into seed eval cases, keeping the failures.
    • Rank open risks with tests, and make an explicit build, iterate or stop decision.

    Progress is saved in this browser and lights up the roadmap.