Skip to main content

What is a prompt file?

A prompt file is the source of truth for a PDD module. It defines the intent, requirements, and constraints for a single code file. PDD reads the prompt file, preprocesses it (resolving directives), then sends the result to an LLM to generate the corresponding code. Prompt files use the extension .prompt and follow a strict naming convention.

Naming convention

<basename>_<language>.prompt
  • <basename> is the name of the module (underscore-separated words)
  • <language> is the target programming language or context
factorial_calculator_python.prompt
The language suffix determines what file extension the generated code receives and how pdd sync detects which language to use. For example, factorial_calculator_python.prompt generates factorial_calculator.py.

Supported languages

Python

JavaScript

TypeScript

Java

C++

Ruby

Go

CSS / other

Files ending in _llm.prompt are reserved for PDD’s internal processing and cannot form valid development units. They lack the associated code, examples, and tests required for the sync workflow.

Prompt structure

A well-designed prompt contains only what cannot be handled elsewhere. With cloud grounding and accumulated tests, prompts can be minimal — aim for 10–30% of your expected code size.

Required sections

  1. Role and scope (1–2 sentences): what this module does
  2. Requirements (5–10 items): functional and non-functional specs
  3. Dependencies (via <include>): external or critical interfaces

Optional sections

  1. Instructions: only if default behavior needs overriding
  2. Deliverables: only if non-obvious

Example structure

factorial_calculator_python.prompt
% You are an expert Python engineer. Create a module `factorial_calculator`
% that computes factorials efficiently.

<include>context/python_preamble.prompt</include>

% Requirements
  1. Function: factorial(n: int) -> int
  2. Input: non-negative integer n
  3. Output: exact integer result (not float)
  4. Raise ValueError for negative inputs
  5. Handle n=0 correctly (returns 1)
  6. Performance: iterative implementation, O(n) time

% Dependencies
  <math_utils>
    <include>context/math_utils_example.py</include>
  </math_utils>

% Instructions
  - Export only the `factorial` function
  - Add a brief docstring with examples
Lines beginning with % are human-readable section headers. PDD passes them to the LLM as-is; they help structure the context without requiring any special processing.

PDD directives

Directives are special XML-style tags that PDD’s preprocessor resolves before sending the prompt to the LLM. The LLM sees the resolved content, not the tags themselves. Processing order: <pdd><include> / <include-many><shell><web>

<include>

Injects the content of a file at that position in the prompt.
<include>context/python_preamble.prompt</include>
Use <include> when:
  • Including a shared preamble that applies to all prompts
  • Referencing a dependency’s example file as an interface
  • Pulling in authoritative documentation (README, API schema, config reference)
<!-- Optional include: missing file becomes empty string, with a warning -->
<include optional>context/optional_config.md</include>

<!-- Include only a specific function from a Python file -->
<include select="def:parse_user_id">src/utils.py</include>

<!-- Include only a class definition (interface mode: signatures only, no bodies) -->
<include select="class:BillingService" mode="interface">src/billing/service.py</include>

<!-- Include a markdown section -->
<include select="section:Environment Variables">docs/config.md</include>

<!-- Include multiple files -->
<include-many>context/preamble.prompt, context/logger_example.py</include-many>
Wrapping includes in semantic tags makes dependencies scannable:
<billing_service>
  <include>context/billing_service_example.py</include>
</billing_service>
Automatic update propagation: when the included file changes, all prompts that reference it automatically reflect those changes on the next generation — without editing the prompts themselves.

<shell>

Executes a shell command and inlines stdout at that position.
<shell>git config --get user.name</shell>
On non-zero exit, PDD inserts a bracketed error note instead of failing the pipeline.

<web>

Fetches a URL (via Firecrawl) and inlines the markdown content.
<web>https://docs.litellm.ai/docs/completion/json_mode</web>

<pdd> (comment)

A human-only comment. Removed entirely during preprocessing; never reaches the LLM.
<pdd>TODO: tighten the performance constraint once benchmarks are done</pdd>
<shell>, <web>, and <include select="..." query="..."> introduce non-determinism — output varies by environment or time. The same prompt file can produce different generations on different machines. Prefer capturing output to a static file and using <include> for reproducibility.

The shared preamble pattern

A shared preamble is a file (conventionally context/project_preamble.prompt) included at the top of every prompt in the project. It defines your “constitution”: coding style, linting rules, naming conventions, and forbidden libraries.
<include>context/project_preamble.prompt</include>
This keeps individual prompts focused on requirements rather than style. When standards evolve, a single edit to the preamble file updates all prompts on the next generation. What belongs in a preamble:
  • Indentation and formatting rules
  • Preferred import conventions
  • Forbidden libraries or patterns
  • Logging and error handling standards
  • Security baseline requirements
What does NOT belong in a preamble:
  • Module-specific requirements or constraints
  • Business logic or domain rules
  • Dependency references specific to one module

Parameterized prompts

Prompt files can contain template variables using $VAR or ${VAR} syntax. Values are provided at generation time via the -e / --env flag.
module_python.prompt
% You are an expert Python engineer. Create a module `$MODULE`.

% Requirements
  1. Implement the core logic for the ${MODULE} domain
  ...
# Generate a single module
pdd generate -e MODULE=orders --output 'src/${MODULE}.py' prompts/module_python.prompt

# Generate multiple files from the same prompt
pdd generate -e MODULE=orders   --output 'src/${MODULE}.py' prompts/module_python.prompt
pdd generate -e MODULE=payments --output 'src/${MODULE}.py' prompts/module_python.prompt
pdd generate -e MODULE=customers --output 'src/${MODULE}.py' prompts/module_python.prompt
Rules:
  • Only variables explicitly provided via -e are substituted. All other $ text is left unchanged.
  • -e KEY (no =VALUE) reads the value from the current shell environment.
  • -e values take precedence over same-named OS environment variables during template expansion.
Quote --output paths with single quotes to let PDD expand variables rather than the shell: --output 'src/${MODULE}.py'

Architecture metadata tags

Prompt files can embed optional metadata tags that sync with architecture.json. These enable bidirectional sync between prompt files and the architecture visualization.
<pdd-reason>Provides unified LLM invocation across all PDD operations.</pdd-reason>

<pdd-interface>
{
  "type": "module",
  "module": {
    "functions": [
      {"name": "llm_invoke", "signature": "(prompt, strength, ...)", "returns": "Dict"}
    ]
  }
}
</pdd-interface>

<pdd-dependency>path_resolution_python.prompt</pdd-dependency>
<pdd-dependency> declares architectural dependencies that update architecture.json. It is distinct from <include>, which injects file content into the prompt for the LLM’s context. Use both when appropriate — they serve different purposes.

Writing effective requirements

Requirements are the core of your prompt. Everything else — coding style, implementation patterns, edge cases — is handled by the shared preamble, grounding, and accumulated tests respectively.

Structure (aim for 5–10 items)

  1. Primary function: what does this module do? (one sentence)
  2. Input contract: types, validation rules, what’s accepted
  3. Output contract: types, error conditions, return values
  4. Key invariants: what must always be true
  5. Performance constraints: if any (latency, memory, complexity)
  6. Security constraints: if any (input sanitization, auth requirements)

Before and after

1. Create a UserValidator class with validate() method
2. Use snake_case for all methods       ← belongs in preamble
3. Import typing at the top             ← belongs in preamble
4. Add docstrings to all public methods ← belongs in preamble
5. Handle null by returning ValidationError
6. Handle empty string by returning ValidationError
7. Handle whitespace-only by returning ValidationError
Each requirement should be testable. If you cannot write a test for a requirement, it is too vague.

What not to include

Do not specifyReason
Coding style (naming, formatting, imports)Handled by shared preamble
Implementation patterns (class structure, helpers)Handled by grounding
Every edge caseHandled by accumulated tests
Step-by-step implementation instructionsLet the LLM decide unless critical
If your prompt exceeds 30% of expected code size, ask: am I specifying something that preamble, grounding, or tests should handle instead?

Build docs developers (and LLMs) love