Context Engineering and Prompt Management

Context Engineering and Prompt Management

Prompt Templates — Patterns

Prompt templates › Patterns

7 of 57 in this book · 3 min read

Fail-fast rendering with a declared hole set

Problem. An undefined or empty value usually renders as nothing. The result is still valid text, the model still answers, and the defect surfaces days later as an unexplained drop in answer quality rather than as an error.

Approach. Declare the hole set explicitly alongside the template — each name, its type, and whether it is required. Configure the engine so an undefined name raises rather than renders empty, and validate supplied values against the declaration before rendering. Where a value is legitimately optional, make the template's handling of its absence explicit rather than incidental, so the no-value rendering is a reviewed sentence rather than a gap.

Trade-off. Strictness converts a silent degradation into a request failure, which is better diagnostically and worse for availability; callers need a defined behaviour when a non-critical source has nothing to contribute, or the strictness is routed around with empty strings.

Untrusted-value substitution boundary

Problem. Rendering is the moment text from outside the system becomes part of the prompt. A document title, a filename, or a pasted message can carry delimiters, role markers, or instructions, and after substitution there is no way to tell which characters came from the author and which came from the input.

Approach. Type the holes by trust rather than by shape. Values from a trusted source are substituted as-is; values from any external source pass through a normalising transform first — collapsing control and invisible characters, neutralising the engine's own delimiters, and wrapping the value in an unambiguous container the template owns. Record which holes were filled from untrusted sources on the rendered artefact.

Trade-off. Neutralisation alters content the model may need to see verbatim, and a container the template owns is only unambiguous until a value happens to contain it. This reduces the attack surface at the substitution point; it is not a substitute for treating the resulting span as untrusted downstream.

Golden renderings as the review artefact

Problem. A template is reviewed as source, but what the model receives is the rendering. A diff that looks like a two-word change can alter indentation, drop a blank line before a schema, or change the order of two sections through an include.

Approach. Keep a small set of fixture inputs with the template and commit the rendered output for each. A change to the template produces a diff in the renderings, which is what reviewers read. The same fixtures double as the regression check that an engine upgrade did not silently change whitespace handling.

Trade-off. Fixtures go stale, and a large template family generates a lot of committed text that reviewers learn to skim. Fixtures also only cover the branches someone thought to write, so the rarely-taken paths remain unreviewed exactly where they are most likely to be wrong.

Fragment library instead of forked templates

Problem. Two surfaces need the same prompt with one differing paragraph. Copying the template is quick and guarantees that the next shared change is applied to one copy and forgotten in the other.

Approach. Factor the shared body into named fragments included by both templates, and keep the differences at the top level where they are visible. Treat each fragment as having its own owner and its own fixtures, and make the include graph printable so the effect of editing a fragment is knowable before the edit.

Trade-off. Indirection makes any single template harder to read end to end, and a fragment edited for one caller changes every other caller silently unless each has its own golden rendering. Deep include graphs reproduce, in prose, the fragility of deep inheritance in code.