Defensive Prompt Engineering
Prompt metadata is data about a prompt, such as model_name, date_created, prompt_text, application, and creator, that tells you what the prompt is for and lets you search for it.
Why a Bare Prompt String Is Not Enough
Imagine a team with one small project and one prompt sitting in one file. That arrangement is easy to manage. Now imagine the organization runs many prompts across several applications. Someone who finds a prompt string with no surrounding information cannot tell which model it was written for, when it was created, which application uses it, or who is responsible for it. Each of those unanswered questions has a practical cost when something goes wrong in production.
Defensive prompt engineering is the habit of attaching enough information to every prompt that its purpose, owner, and intended use can be recovered later, even after the original author has moved on. This article follows that idea from a single Python object up to the way teams manage whole collections of prompts over time.
Predicting What a Bare Prompt Cannot Answer
What do you think happens?
Given only the prompt text, which of these questions can you answer: which model it was written for, when it was created, which application uses it, or who is responsible for it?
Reveal answer
None of them
A prompt string with no surrounding information cannot tell you which model it was written for, when it was created, which application uses it, or who is responsible for it. The answers have to be stored alongside the text as metadata.
Metadata as Labels on a Prompt
Prompt metadata is data about a prompt rather than the prompt's own words. Metadata such as model_name, date_created, prompt_text, application, and creator tells you what the prompt is for, and it lets you search for it.
The five fields work together. The prompt_text field holds the words the model receives. The model_name field records which model the prompt was written for. The date_created field records when it was made. The application field names the program that uses it, and the creator field names the person or group responsible for it. Good metadata lets you answer the question what is this prompt for, and it also lets you organize prompts so you can search for them by model, by application, and by other attributes.
Bundling Text and Metadata in One Object
One way to attach metadata is to wrap each prompt in a Python object. The object holds the prompt text next to the fields that describe it. The class below uses Pydantic's BaseModel, which lets you declare the type of each field. Read the field names first and try to say, in your own words, what each one records.
- The class declares each field and its type in one place
- Each field is declared without a default value
- The object keeps the prompt text and its description together as one unit
Each field has a declared type, and Pydantic uses those declarations to check values when a Prompt is created. That check is the main reason to use a model class instead of a plain dictionary. The structure of every prompt record is written down in one place, so a prompt that lacks a field is caught at creation time rather than discovered later during a search.
Predicting the Result of a Missing Field
What do you think happens?
If you create a Prompt without a creator field, when does the problem get noticed?
Reveal answer
Immediately, when the Prompt object is created
Every field is declared without a default value, so each one is required. Because the structure is checked at creation time, the missing field is caught then rather than discovered later during a search.
- Every field except creator is supplied
- Creator has no default, so the class cannot build a complete record from these arguments
Tracing a Prompt Through Creation
Tracing the broken_prompt creation
Follow what happens when the Prompt object from the previous example is created without a creator value.
- 1Read the arguments
prompt_text,model_name,date_created, and application are all supplied; creator is not. - 2Check each declared field
The class checks each declared field against the supplied values. The first three fields pass, and application passes.
- 3Reach the missing field
creator is declared without a default, so there is no value to fall back on.
- 4Refuse to build the record
The incomplete object is not accepted. The problem is reported at the line where the object is created.
The prompt never enters the collection as a broken record. Had the class accepted it, the missing creator would have surfaced only when someone later searched for the prompt's owner.
Operational Details That Belong With the Prompt
The five identity and ownership fields are not the whole story. A prompt template may also carry operational details about how it should be used. The source describes three additional kinds of information that a template might contain.
- The model endpoint URL, which identifies where the prompt is sent
- Sampling parameters such as temperature or top-p, which control how the model chooses its output
- The expected output schema for structured outputs, which describes the shape the response must take
These operational details belong with the prompt for the same reason the identity fields do. A prompt that expects a particular output structure is incomplete without a record of that expectation, and a prompt tuned with specific sampling settings behaves differently when those settings are lost. Keeping them in the same record as the text means the prompt carries what it needs to be used correctly.
Some tools have gone further and proposed dedicated file formats for storing prompts. The source names Google Firebase's Dotprompt, Humanloop, Continue Dev, and Promptfile. Whatever format a team picks, the goal is the same: the prompt and its metadata are stored together as one unit.
Versioning in Git Versus a Prompt Catalog
Metadata tells you what a prompt is. Versioning tells you which revision of the prompt a given application is running. If prompt files live inside your git repository, you can version them with git, just as you version the rest of your code. Every change to a prompt file becomes a commit, and the history of that file records how the prompt evolved.
The limitation appears when several applications share one prompt file. A shared prompt update forces every dependent application onto the new version. Many teams solve this shared-file problem with a separate prompt catalog, which is its own store of prompts, independent of any one application's code. The catalog explicitly versions each prompt, so different applications can pin themselves to different prompt versions. One application can stay on version one while another moves to version two, and neither decision forces the other.
| Question | Git repository | Separate prompt catalog |
|---|---|---|
| Where does the prompt live? | Inside the application's repository | In its own store, independent of any one application's code |
| How is a version identified? | By the history of commits to the prompt file | Explicitly, because the catalog versions each prompt |
| What happens when it changes? | A shared update forces every dependent application onto the new version | Applications stay pinned to the versions they chose |
| How do owners learn of changes? | Not described in the source | A well-built catalog may notify application owners of a newer version |
| Can you search for prompts? | Not a built-in capability described in the source | Yes, the catalog supports search and provides metadata |
Git versioning compared with a separate prompt catalog
Spotting Divergence From Expectations
Suppose two applications were supposed to use the same summarization prompt, but one of them now produces output that looks different from what the team expected. The source suggests a method for finding where the divergence came from. Start from the record for each prompt and ask what the metadata says about the application, the model, and the creator. Then ask which version each application is actually using.
Finding the source of a changed summary
Two applications share a summarization prompt. One now produces output that looks different from what the team expected. Where should the investigation start?
- 1Compare the application fields
Check the application field on each prompt record to confirm which program uses which record.
- 2Compare the model names
A difference in
model_namebetween the two records is a strong lead, because the same text can behave differently on a different model. - 3Check the pinned version
Find which version each application is actually using. A difference in the version pinned by one application narrows the search quickly.
- 4Read the prompt text last
Rereading the text is the slowest route. The metadata and version history often explain the difference before the wording needs to be examined.
The divergence is traced to a metadata or version difference, such as a different model_name or a different pinned version, rather than to a guess about the prompt wording.
Mistakes That Undermine Prompt Traceability
Common mistake Storing the prompt text alone
What it looks likeA prompt string saved in a file with no model, date, application, or creator recorded
Why it failsNobody can tell which model it was written for, when it was created, which application uses it, or who is responsible
Do this insteadStore the prompt together with its metadata as one unit
Common mistake Using a plain dictionary and assuming every record is complete
Why it failsA plain dictionary does not write the structure of each record in one place, so a missing field may go unnoticed until a search fails
Do this insteadDeclare the fields in a model class so a missing field is caught at creation time
Common mistake Leaving out operational details
What it looks likeSaving the text but not the expected output schema or sampling settings
Why it failsA prompt that expects a particular output structure or uses specific sampling settings behaves differently when those details are lost
Do this insteadKeep the endpoint URL, sampling parameters, and output schema in the same record
Common mistake Assuming a shared git file is safe for every application
Why it failsA shared prompt update forces every dependent application onto the new version
Do this insteadUse a separate prompt catalog that lets applications pin different versions when they need to
Common mistake Rereading the prompt text first when output changes, instead of comparing metadata and pinned versions
Practice Tracing a Prompt Record
A teammate creates a Prompt object for a translation prompt and supplies prompt_text, model_name, and creator, but leaves out date_created. Using the Prompt class from this article, predict whether the object is accepted, and explain which field is responsible and at what point the problem is caught. Then describe one metadata field you would compare first if the translation output later changed unexpectedly.
Hints
- Recall that every field in the class is declared without a default value.
- Think about when the class checks the declared fields, not when the prompt is later searched.
- Consider which metadata field tells you which program uses the prompt, and which tells you which model it was written for.
Key Takeaways
- Prompt metadata such as
model_name,date_created,prompt_text, application, and creator records what a prompt is for and makes it searchable. - A Python model class holds the prompt text with its metadata and checks declared fields at creation time, so a missing required field is caught immediately rather than during a later search.
- Versioning prompts in git ties every change to a commit, but a shared update forces every dependent application onto the new version; a separate prompt catalog versions each prompt explicitly so applications can pin different versions.
- When output diverges from expectations, compare the metadata and pinned versions in each prompt record before rereading the prompt text.