Course Content
Live Coding Interview Prep
7 sections · 50 lessons
Build a prompt versioning system.
What you need to know
Prompts change behaviour as much as code does, but they are often edited in a dashboard with no history. When quality drops on Tuesday, nobody can say which prompt was live on Monday. Prompt versioning borrows three ideas from software releases:
- Immutable versions. Version 3 always means the same text. A change creates version 4.
- Environment pointers.
stagingandprodeach point at a version. Promotion is moving a pointer; nothing is copied or edited. - Rollback to what was live. Keep the history of each pointer, so rollback returns to the previous active version, not simply "version number minus one".
A fingerprint (a short hash of the template plus the model name) identifies content exactly. It catches the case where someone registers the same text again, and it lets you prove that two environments run the same prompt.
1import hashlib, time2from dataclasses import dataclass, field34@dataclass(frozen=True)5class PromptVersion:6 name: str7 version: int8 template: str9 model: str10 created_at: float = field(default_factory=time.time)1112 @property13 def fingerprint(self) -> str:14 return hashlib.sha256(f"{self.model}\n{self.template}".encode()).hexdigest()[:12]1516class PromptRegistry:17 """Immutable prompt versions, per-environment pointers, history-based rollback."""1819 def __init__(self) -> None:20 self.versions: dict[str, dict[int, PromptVersion]] = {}21 self.history: dict[tuple[str, str], list[int]] = {} # (name, env) -> active stack2223 def register(self, name: str, template: str, model: str) -> PromptVersion:24 by_number = self.versions.setdefault(name, {})25 candidate = PromptVersion(name, len(by_number) + 1, template, model)26 for existing in by_number.values():27 if existing.fingerprint == candidate.fingerprint:28 return existing # same content: no new version29 by_number[candidate.version] = candidate30 return candidate3132 def promote(self, name: str, version: int, env: str = "prod") -> None:33 if version not in self.versions.get(name, {}):34 raise KeyError(f"{name} v{version} does not exist")35 stack = self.history.setdefault((name, env), [])36 if not stack or stack[-1] != version:37 stack.append(version)3839 def get(self, name: str, env: str = "prod") -> PromptVersion:40 stack = self.history.get((name, env))41 if not stack:42 raise KeyError(f"no active version of {name!r} in {env!r}")43 return self.versions[name][stack[-1]]4445 def rollback(self, name: str, env: str = "prod") -> PromptVersion:46 stack = self.history.get((name, env), [])47 if len(stack) < 2:48 raise ValueError("nothing to roll back to")49 stack.pop()50 return self.get(name, env)The tricky parts:
frozen=Truemakes a version object immutable in Python, matching the rule that a version never changes.- Duplicate check against all versions, not just the latest. Re-registering v1's text after v2 returns v1 instead of inventing a v3 that is secretly identical.
- The history stack. If prod went v1 → v3 (v2 was only ever tested in staging), rollback must return to v1. "Current minus one" would put the never-deployed v2 into production.
Complexity: register is O(v) because of the duplicate scan (keep a fingerprint-to-version dict to make it O(1)); promote, get and rollback are O(1). Space is O(total template size).
A real-life example
1reg = PromptRegistry()2v1 = reg.register("refund_bot", "You are a polite refund assistant.", "claude-opus-5")3v2 = reg.register("refund_bot", "You are a refund assistant. Be brief.", "claude-opus-5")4v3 = reg.register("refund_bot", "You are a refund assistant. Cite policy.", "claude-opus-5")5same = reg.register("refund_bot", "You are a polite refund assistant.", "claude-opus-5")6print(same.version) # 1 (identical content, no new version)78reg.promote("refund_bot", 1) # prod: [1]9reg.promote("refund_bot", 2, env="staging") # staging only10reg.promote("refund_bot", 3) # prod: [1, 3]11print(reg.get("refund_bot").version) # 312print(reg.rollback("refund_bot").version) # 1, not 213print(reg.get("refund_bot", "staging").version, v3.fingerprint != v1.fingerprint) # 2 True| action | prod stack | staging stack | live in prod |
|---|---|---|---|
| promote v1 | [1] | – | v1 |
| promote v2 to staging | [1] | [2] | v1 |
| promote v3 | [1, 3] | [2] | v3 |
| rollback | [1] | [2] | v1 |
A support team that changed its refund-bot prompt on Friday and saw complaints on Saturday can find the exact version in the logs and roll back in one call, without redeploying code.
Follow-up questions to expect
- "Where does this live in production?" — A database table with columns for name, version, template, model, author and eval score, or a git directory of YAML files where a merge is a new version. Either way the app reads the active pointer at request time.
- "Two people promote at once?" — The pointer update needs a transaction or a compare-and-set (
UPDATE … WHERE active_version = expected), so one of them fails loudly instead of both "winning". - "How do you connect versions to quality?" — Log name, version and fingerprint on every request next to the trace id, and store eval results per version, so a quality change maps to a prompt change in one query.