The code-generation skeleton

Worked example: the dedupe function done right

5 min read

Here is the same dedupe task from lesson 1, rewritten using the four-block skeleton:

Write a Python function dedupe_preserve_order(items: list[str]) -> list[str].

Constraints:

  • O(n) time and O(n) extra memory.
  • Preserve first-seen order.
  • Case-sensitive ("Apple" and "apple" are distinct).
  • No external imports.
  • Type hints required.

Include 4 assert test cases inside an if __name__ == "__main__": block:

  1. Empty list returns [].
  2. Single-item list returns the same list.
  3. ["a","b","a","c","b"] returns ["a","b","c"].
  4. Case-sensitivity: ["Apple","apple","Apple"] returns ["Apple","apple"].

Output ONLY a single Python code block, no prose around it.

Here is what Claude Sonnet 4.5 returned:

def dedupe_preserve_order(items: list[str]) -> list[str]:
    seen: set[str] = set()
    result: list[str] = []
    for item in items:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result


if __name__ == "__main__":
    assert dedupe_preserve_order([]) == []
    assert dedupe_preserve_order(["a"]) == ["a"]
    assert dedupe_preserve_order(["a", "b", "a", "c", "b"]) == ["a", "b", "c"]
    assert dedupe_preserve_order(["Apple", "apple", "Apple"]) == ["Apple", "apple"]

Captured from Claude Sonnet 4.5 (claude-sonnet-4-5) on 2026-04-27. Re-runs may differ slightly.

The reasoning flow that produced this output:

Compare line-for-line to the lesson 1 output:

Lesson 1 (vague prompt)Lesson 5 (skeleton)
Two functions with the same nameOne function, one signature
No type hintsFull type hints, including seen: set[str]
No testsFour asserts, one per property
Markdown headings, prose, two implementationsSingle code block, nothing around it
O(n) and O(?) versions both shownOne O(n) implementation, locked by the constraint

You can python dedupe.py this file directly. If the asserts pass, you can commit. That is the difference one well-shaped prompt makes — six minutes of careful prompt-writing instead of forty-five minutes of accept-edit-test-fix loops.

A note on what didn't happen: the model did not over-engineer. It did not add a Generic[T] type variable, did not write a docstring, did not handle None, did not add a CLI wrapper. Why? Because none of those were in INTENT or CONSTRAINTS. The skeleton's job is to give the model exactly enough room to do the work, and no more.

When you start using this skeleton on real code, you'll find the constraint list growing as you encounter team conventions: "match our existing style: 2-space indent, no trailing commas, prefer composition over inheritance." Save those as a snippet. The constraint list becomes a portable expression of how your team writes code.

Next module: applying the same discipline to debugging. :::

Quiz

Module 1: The Codegen Skeleton

Take Quiz
Was this lesson helpful?

Sign in to rate

FREE WEEKLY NEWSLETTER

Stay on the Nerd Track

One email per week — courses, deep dives, tools, and AI experiments.

No spam. Unsubscribe anytime.