A prompt that lives in one engineer's head is not a prompt. It is a local habit. It works until that person goes on leave, another team copies the string into a second service, and nobody can say which wording is live for checkout summaries on Tuesday afternoon.
Prompt versioning sounds like paperwork until you have shipped a "quick wording tweak" that changed tool-calling behavior in production. Then it sounds like the minimum bar for not lying to yourself.
What breaks without versions
Without a version, you cannot tie a bad answer to a change. Logs show the model and the user input. They do not show whether the system message still mentioned the refund policy that legal rewrote last week. Eval runs become folklore: someone says the suite looked fine on their laptop against "the latest prompt," and the latest prompt was whatever was in a staging ConfigMap at 4pm.
Multiplayer makes it worse. Two engineers improve different paragraphs. Both merge. One improvement undoes the constraint the other added. Or worse, both improvements ship to different services because the prompt was duplicated by hand. You now have product behavior that depends on which code path the request hit, and no shared name for either variant.
Vendors will happily let you type into a playground and export nothing durable. Playgrounds are fine for exploration. They are a poor source of truth for anything that touches money, safety policy, or a customer-visible answer.
Version the artifact, not the vibes
Treat the prompt as a named artifact with an immutable version. The name is stable: support.ticket_triage. The version changes when the text or the structured contract changes: support.ticket_triage@3 or a content hash, as long as the identifier cannot be edited in place.
Store the full prompt body, not a pointer to a chat thread. Include the parts that actually affect behavior: system instructions, tool definitions, response schema, default model parameters that you consider part of the contract, and any few-shot examples you ship. If temperature or max tokens are load-bearing for the feature, they belong with the version. If they are left to each caller, you will spend a month debugging "the same prompt" that was never the same.
Keep the source in git even if you also load it from a config service at runtime. Git gives you review, blame, and a rollback story people already understand. The runtime store gives you fast rollout without waiting for a full service deploy. Those are complementary. Runtime-only edits with no commit are how production accumulates invisible forks.
Make rollout a deliberate act
Shipping a new prompt version should look more like shipping a model change than editing a string in an admin box.
Build or promote a version through environments. Run the eval suite you already trust against that exact version id. Roll out behind a flag or a percentage split when the feature is user-visible. Record which version served each request. When quality or cost moves, you can ask for the version distribution instead of guessing from merge dates.
Keep the previous version runnable. Rollback is not a git revert followed by a prayer if the old bytes are gone from the config store. Retain enough history that you can pin production back to @3 while you inspect @4. If a version is dangerous, mark it disabled. Do not delete it out of tidiness until you are sure no delayed job or forgotten service still requests it.
Dual-running two versions for a while is normal. Dual-running five because nobody cleaned up experiment flags is how you get a museum. Set an owner and an expiry on experiments the same way you would for any other gated rollout.
Contracts between prompts and code
Prompts do not live alone. Application code parses their output, chooses tools, and decides what happens when the model returns something awkward. A prompt version that changes the JSON shape without a code change is a production incident with better prose.
Version the contract with the prompt. If the model must return { "intent", "confidence", "needs_human" }, that schema is part of the artifact. CI should fail when the prompt expects fields the parser does not know, or when the parser requires fields the prompt no longer asks for. This is dull work. It prevents the exciting work of paging at midnight because a model stopped emitting a key after a "clarity pass" on the instructions.
Tool definitions belong in the same bundle. Adding a tool is a behavior change. Removing one is a behavior change. Letting each service invent its own tool list around a shared prompt text is how you get "works in service A" mysteries.
Reviews that catch real risk
A pull request that only shows a wall of English is hard to review. Force the diff to answer a few concrete questions: What behavior should change? What must not change? Which eval cases cover the risk? Did cost per unit move in the smoke run?
Require a short changelog entry next to the version. "Tightened refund wording" is better than silence. "Removed the sentence that forbade inventing order ids" is better still. Future you will not remember why @7 exists. The note should be written for the person who gets paged in three months.
Do not make legal or safety review a surprise gate on Friday evening. If a prompt touches policies those groups own, put them in the review path early. Waiting until the wording is already on 20% of traffic teaches everyone the wrong lesson about process.
Ownership and the copy-paste tax
Every production prompt needs an owning team in a place that is searchable. When cost spikes or quality drops, the alert should name a group that can pin a version, not a Slack channel that used to be active.
Duplication is the tax you pay for moving fast without a shared library. A little duplication is survivable if each copy has its own version history and owner. Silent duplication, where teams believe they share a prompt and do not, is worse. Prefer a single published artifact referenced by id. If a team needs a fork, make them fork explicitly under a new name so nobody thinks the originals still match.
Internal docs that paste the "current prompt" as prose go stale before the ink dries. Link to the version id. If someone needs to read the text, they can open that version. Screenshots of a playground are not documentation.
Enough process to sleep
You can do this without a grand platform. A git directory of prompt bundles, a thin registry that maps ame -> active version` per environment, request logs that include the version id, and an eval job that takes a version id as input will carry a surprising amount of weight.
The goal is narrow: when something regresses, you can name the prompt version that was live, compare it to the prior one, and put the old one back without reconstructing history from memory. That is the difference between prompt tinkering and prompt operations.
If only one engineer can explain what the model was told yesterday, you do not have a prompt system. You have a bus factor of one wearing a language model.