A practice prompt we wrote. No company or candidate report names it, so it carries no company tag.
How to answer
Open with why loud matters: a model given “Summarize the ticket for {{customer_name}}” answers plausibly anyway. Your renderer is the last cheap check.
- Pin the contract. “
render(template, variables) -> str. Missing variables raise one error naming all of them and the template. Unused ones raise too, since they usually mean a renamed placeholder, unless callers share one context across templates; then I warn.” Ask whether an empty string counts and how braces are escaped. RefuseNoneand non-strings:str(None)writes the word None into the prompt. - Say why the standard tools fall short.
str.formatstops at the first missing name and follows attribute and index lookups, so an outside template can reach into your objects. Its single braces also collide with every JSON example, hence{{name}}.string.Template.safe_substituteleaves missing placeholders in place, the silent failure itself. Jinja renders an undefined name as empty text unless you configureStrictUndefined, which stops at the first one. Your own parser is fine, or strict Jinja after collecting every name withfind_undeclared_variables. Say which. - Parse once, at load time. Extract placeholders with one regex; reject bad names and unclosed braces before any request. In CI, a test renders every template file against a fixture of its declared variables.
- Substitute in one pass. Use
re.subwith a function, so each value is inserted once, never rescanned. - Return what helps debugging. The rendered text plus a template hash, so each call logs its prompt version.
- Test the failures first. All missing names in one error, a value full of braces left untouched, an escape, an unused variable, whitespace inside a placeholder.
The trap is for k, v in variables.items(): text = text.replace(...). It passes happy-path tests, then expands a customer name containing {{ticket_body}} into the whole ticket. Practice it in the strict prompt renderer drill.
Follow-ups
What the interviewer may ask next, once your first answer is on the table.
- A customer’s name contains a placeholder, such as “{{ticket_body}}”. What does your renderer produce?
- The caller passes a variable the template never uses. Error, warning or nothing?
- An empty string arrives for a required variable. Is that missing?
- How would you catch a broken template before it reaches production traffic?
Where answers go wrong
- Using a lenient default, such as string.Template.safe_substitute or a defaultdict, so a missing value renders as a literal placeholder or as empty text and the model answers anyway.
- Replacing placeholders one at a time, so a value that contains another placeholder gets expanded too.
- Raising on the first missing name only, so the caller fixes one variable per failed deploy.
- Calling
str()on every value, so aNoneputs the word None in the prompt and a dict arrives as a Python repr.
Answer this in two minutes
Write the answer you would say out loud. The clock starts with your first word.
Model answer
“Here’s the contract I’ll build to. Placeholders are {{name}}, with whitespace allowed inside, so JSON examples with single braces pass through untouched.