Your Prompts Have Foreign Keys
Rename a database column and watch what happens. The compiler catches every query builder. The ORM migration catches the model class. The type checker catches the API serializer. Integration tests catch the two services that read the field over the wire. Every consumer of that column gets flagged — except one. Your system prompt, which contains a carefully formatted description of the table "so the model understands the data," keeps confidently describing a column that no longer exists. No compiler error. No failing test. No deprecation warning. Just a model that starts generating queries against a schema from three sprints ago, and a dashboard that slowly fills with malformed SQL.
Prompts have foreign keys. They reference database schemas, tool signatures, enum values, API shapes, and few-shot examples copied from production data — and none of those references participate in refactoring. Every fact you paste into a prompt is a join against a table that your tooling doesn't know exists. When the referenced artifact changes, nothing cascades. The prompt just rots in place.
The Rot Is Invisible Because Nothing Breaks
The reason this failure mode is so persistent is that it fails silently, and it fails probabilistically. A stale import crashes at startup. A stale prompt degrades at sampling time. When the prompt says the status field takes active | suspended | churned and the enum gained a paused state last quarter, the model doesn't error — it does something worse. It classifies paused customers into whichever of the three stale values seems closest, with full confidence, at whatever rate your traffic happens to exercise that path.
Practitioners who monitor these systems describe the same trajectory: accumulated small mismatches produce a slow, almost invisible degradation that eventually tips into sharp failure. And the tip usually comes from a routine change nobody connected to the AI system at all. A backend engineer renames customer_id to account_id as part of a cleanup ticket. Their PR touches fourteen files, all green. The prompt template living in another repo — or worse, in a prompt-management dashboard outside version control entirely — isn't one of them.
The stale references cluster into a few recognizable species:
- Schema descriptions — table and column documentation pasted into text-to-SQL or analytics prompts, accurate as of the day someone wrote them.
- Tool signatures — prose descriptions of what a tool does and what arguments it takes, drifting away from the actual function as the backend evolves.
- Enum values and business rules — "the plan field is one of free, pro, enterprise" — until the pricing team ships a fourth tier.
- Few-shot examples — real input/output pairs copied from production a year ago, demonstrating formats and field names that no longer exist.
Each of these is a dependency. None of them is declared anywhere a tool can see.
Tool Drift: When the Model's Map Diverges From the Territory
Agent systems make the problem sharper because the references are denser. An agent's effectiveness depends on the model's mental map of its tools matching the actual toolset, and those two things evolve independently. The tool registry grows to twenty-eight tools; the system prompt still narrates three of them. Engineers who have instrumented this describe three distinct symptoms.
First, hallucinated tools. When the prompt's examples mention search_customers and get_invoice_by_id, the model happily extrapolates to a plausible-sounding search_customer_invoices that has never existed. The prompt's stale vocabulary isn't just failing to help — it's actively teaching the model a pattern that generates invalid calls.
Second, orphan tools. Tools that exist in the registry but were added after the prompt was last touched get called rarely or never. They still cost input tokens on every request. You're paying for capability the model has effectively been told not to use.
Third, ambiguous selection. When search_docs_v1 and search_docs_legal both exist and the prompt only describes the world before the split, tool choice becomes a coin flip. The model's prior fills the gap the prompt left, and the prior doesn't know your migration plan.
The stale few-shot example is the most insidious variant because examples outweigh instructions. You can update the instruction paragraph to say "use account_id" while a forgotten example thirty lines down still shows customer_id in its output JSON — and the model will follow the example. Research on few-shot prompting consistently finds that inaccurate examples don't just fail to help; they actively increase error rates. A stale example is an inaccurate example with seniority.
Treat Prompt-Embedded Facts as Dependencies With a Manifest
The fix starts with an inventory. Go through your production prompts and classify every sentence into one of two buckets: opinions and facts. Opinions are the stuff prompts are actually for — tone, priorities, reasoning strategy, guardrails, what to do when uncertain. Facts are claims about the state of an external system: this table has these columns, this tool takes these arguments, this field accepts these values, here is a real example of a valid response.
Opinions can be hand-written and hand-maintained, because only humans hold them. Facts should never be hand-maintained, because the system of record for a fact is never the prompt — it's the schema, the type definition, the OpenAPI spec, the enum declaration. Every hand-written fact in a prompt is a cache with no invalidation strategy.
Once you see the split, the manifest almost writes itself. For each prompt, declare what it depends on: which tables, which tools, which enums, which example sources. The declaration doesn't need exotic tooling — a YAML block next to the prompt template listing artifact identifiers and the commit or schema version they were sourced from is enough to start. What the manifest buys you is the same thing a lockfile buys you: when the upstream artifact changes, you can now know the prompt is affected, instead of finding out from a customer.
This is the same maturity step application code took decades ago. Nobody vendors a dependency by pasting its source into their repo and hoping to remember to re-paste it after upgrades — yet that is exactly what pasting a schema description into a prompt is. Treating prompts as de facto code, with the engineering rigor that implies, is the emerging consensus among teams running LLMs at scale; the dependency manifest is what that rigor looks like applied to a prompt's outbound references.
Generate the Volatile Sections at Deploy Time
A manifest tells you when the prompt is stale. The stronger move is making staleness structurally impossible: stop writing the volatile sections by hand and generate them from the source of truth at build or deploy time.
The prompt template stops being a monolithic string and becomes exactly what your HTML templates became twenty years ago — static authored content with typed slots for dynamic content:
- The schema section is rendered from the live information schema or your migration-managed model definitions, at deploy time, by the same pipeline that ships the code.
- The tool section is rendered from the tool registry's JSON schemas — the same objects you already hand to the API — so prose descriptions and actual signatures cannot diverge.
- Enum inventories are rendered from the enum declarations themselves.
- Few-shot examples are promoted from a curated, tested example store — each example validated against the current output schema in CI — rather than pasted in as string literals.
Teams doing serious text-to-SQL work landed on this architecture out of necessity: schema metadata is retrieved and injected at query time rather than frozen into the prompt, because at production scale hand-maintained schema descriptions are stale within weeks. The same logic applies to every other fact category. If a human wrote a fact into a prompt by hand, you've scheduled an incident; the only unknown is the date.
Generation also shrinks prompts, which pays for itself. Hand-maintained sections accrete defensively — nobody deletes the description of a table because nobody is sure it's unused. Generated sections include exactly what the manifest declares and nothing else.
CI That Diffs Prompt Claims Against Live Schemas
Generation covers the sections you've migrated. For everything still hand-written — legacy prompts, opinion sections that mention field names in passing, that one prompt owned by a team that hasn't bought in yet — the safety net is a CI job that cross-examines prompts against reality.
The checks are unglamorous and highly effective, roughly in order of value:
- Identifier extraction and existence checking. Parse prompts for things shaped like table names, column names, tool names, and enum values; verify each against the live schema, tool registry, and type definitions. A prompt referencing a column that no longer exists is a build failure, exactly like an unresolved import.
- Reverse coverage. Flag tools in the registry that no prompt mentions and no traffic calls. Zero calls over a thousand requests means dead weight or a description the model can't use — either way, tokens you're paying for.
- Example validation. Every few-shot example's output must parse against the current response schema. This single check retires the "instruction says X, example demonstrates Y" class of bugs.
- Drift telemetry in production. Track invalid tool-call rates, out-of-enum classification outputs, and SQL referencing nonexistent columns — and alert on trend, not threshold. Rising invalid-reference rates after a deploy are the runtime signature of a foreign-key violation in a prompt.
None of this requires an ML platform team. The first check is an afternoon of regex and a schema dump. What it requires is the mental reclassification: prompt-to-schema references are dependencies, dependencies get resolved and verified by machines, and a reference no machine verifies is a reference that is already rotting.
The uncomfortable truth underneath all of this is that the industry spent two years versioning prompts — registries, rollbacks, change review — and versioning solves the wrong half of the problem. Version control tells you what the prompt said and when it changed. It says nothing about whether what the prompt says is still true. A perfectly versioned, carefully reviewed, unchanged-for-six-months prompt is precisely the one most likely to be describing a world that no longer exists. Referential integrity, not change tracking, is the property that keeps prompts honest — and it's time our prompts got the foreign-key constraints the rest of our data got in 1970.
- https://dev.to/gabrielanhaia/tool-definition-drift-when-your-agents-toolset-outgrows-its-prompt-k32
- https://www.comet.com/site/blog/prompt-drift/
- https://deepchecks.com/llm-production-challenges-prompt-update-incidents/
- https://aws.amazon.com/blogs/big-data/enriching-metadata-for-accurate-text-to-sql-generation-for-amazon-athena/
- https://www.vellum.ai/blog/how-to-reduce-llm-hallucinations
