Skip to main content
In PDD, prompts are the source of truth. Code is a generated artifact. This page covers how to write prompts that are concise, explicit, and designed for reproducible regeneration.

Quickstart in 5 minutes

1

One prompt = one module

Map each prompt to a single file or narrowly scoped module. Don’t try to generate an entire subsystem in one prompt.
2

Use the standard template

Start with four sections: Role, Requirements, Dependencies, Instructions.
3

Include context explicitly

Use <include>path/to/file</include> to give the model only what it needs. This is a PDD directive — the tool replaces the tag with actual file content before the LLM sees it.
4

Regenerate, don't patch

If the code is wrong, fix the prompt and run pdd fix. Don’t edit generated code directly unless you also update the prompt.
5

Verify

Run pdd sync or pdd verify to confirm the generated code matches the prompt’s intent.

Prompt file naming

Prompt files follow this convention:
<basename>_<language>.prompt
Prompt fileGenerated output
factorial_calculator_python.promptfactorial_calculator.py
responsive_layout_css.promptresponsive_layout.css
data_processing_pipeline_python.promptdata_processing_pipeline.py

Prompt structure template

% You are an expert <language> engineer. Your goal is to implement <module_name>.

<include>context/project_preamble.prompt</include>

% Role & Scope
  One sentence describing what this module does.

% Requirements
  1. Function: <function_signature> → <return_type>
  2. Input contract: accepted types, validation rules
  3. Output contract: return values, error conditions
  4. Key invariants: what must always be true
  5. Performance constraints (if any)
  6. Security constraints (if any)

% Dependencies
  <dependency_name>
    <include>context/dependency_example.py</include>
  </dependency_name>

% Instructions
  - Implement in <output_path>
  - Export <public_api>
  - <any overrides to default behavior>

Target size: prompt-to-code ratio

Aim for 10–30% of your expected output code size:
RatioMeaning
Less than 10%Too vague — missing contracts, error handling, or key constraints
10–30%Just right — requirements and contracts without implementation details
More than 50%Too detailed — duplicating what the preamble, grounding, or tests should handle

Context engineering

Context engineering is the practice of curating exactly what information fits into the model’s context window. The goal is to include everything the model needs and nothing it doesn’t. What to include:
  • Shared preamble (coding style, conventions, forbidden libraries)
  • Dependency interfaces (the example file, not the full implementation)
  • Authoritative documentation (schema docs, API references)
What to leave out:
  • The full codebase
  • Implementation patterns (handled by automated grounding on PDD Cloud)
  • Every edge case (accumulated by tests over time)
Avoid dumping large code files into your prompt. A 500-line module may have a 50-line example that conveys the same interface at one-tenth the token cost.

<include> directives

The <include> tag injects file content into the prompt before it reaches the LLM.
<!-- Single file -->
<include>context/project_preamble.prompt</include>

<!-- Optional: treat missing file as empty string -->
<include optional>context/experimental_config.prompt</include>

<!-- Multiple files -->
<include-many>context/auth_example.py, context/db_example.py</include-many>

<!-- Wrap with a semantic tag for clarity -->
<billing_service>
  <include>context/billing_service_example.py</include>
</billing_service>

Selective includes

Use select= to include only specific parts of a file:
<include select="def:parse_user_id">src/utils.py</include>
<include select="class:User">src/models.py</include>
<include select="section:Environment Variables">docs/config.md</include>
<include select="lines:10-50">src/config.py</include>
<include select="pattern:/^API_.*=/">src/constants.py</include>
SelectorFile typesExample
lines:N-MAnylines:10-20
def:namePythondef:process_request
class:NamePythonclass:UserModel
class:Name.methodPythonclass:UserModel.validate
section:HeadingMarkdownsection:Installation
pattern:/regex/Anypattern:/^import/
path:key.nestedJSON/YAMLpath:config.database.host
Use mode="interface" to extract only signatures, docstrings, and type hints (Python only):
<include select="class:BillingService" mode="interface">src/billing/service.py</include>

Automatic propagation

When an included file changes, every prompt that references it reflects those changes on the next generation — without editing the prompts themselves. This is particularly useful for shared preambles and evolving API contracts.

Shared preambles

Create a context/project_preamble.prompt file and include it at the top of every prompt. Use it to define your “constitution”:
  • Indentation and naming conventions
  • Preferred linting rules
  • Forbidden libraries or patterns
  • Logging and error-handling standards
<include>context/project_preamble.prompt</include>
This keeps individual prompts clean and ensures all generated code follows consistent conventions.

Parameterized prompts

Use $VAR or ${VAR} substitution to generate multiple files from one prompt:
# Generate three modules 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

# Multiple variables
pdd generate -e MODULE=orders -e PACKAGE=core --output 'src/${PACKAGE}/${MODULE}.py' prompts/module_python.prompt

# Docker-style env fallback (reads from shell environment)
export MODULE=orders
pdd generate -e MODULE --output 'src/${MODULE}.py' prompts/module_python.prompt
Only variables passed with -e are substituted. All other $ occurrences are left unchanged.

Writing effective requirements

Requirements are the core of your prompt. Aim for 5–10 items. Structure each requirement to be:
  • Testable — if you can’t write a test for it, it’s too vague
  • Behavioral — describe WHAT, not HOW
  • Unique — don’t duplicate what the preamble or tests already encode
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

Common patterns

Dependency injection via auto-deps

Run pdd auto-deps to discover and insert relevant dependencies automatically:
pdd auto-deps my_module_python.prompt "context/"
PDD scans the directory for example files and documentation, inserts <include> directives, and removes inline content that duplicates what the included files already provide.

Grounding via few-shot examples (PDD Cloud)

On PDD Cloud, generation automatically retrieves the closest prior (prompt, code) pair as a few-shot example. This keeps regenerated code consistent with your established style without any configuration. For explicit control:
<!-- Force a specific example to always be included -->
<pin>billing_service</pin>

<!-- Block a specific example from being retrieved -->
<exclude>legacy_auth_module</exclude>

Architecture metadata tags

Place these tags at the top of prompt files to keep architecture.json in sync:
<pdd-reason>Provides unified LLM invocation across all PDD operations.</pdd-reason>

<pdd-dependency>llm_invoke_python.prompt</pdd-dependency>
<pdd-dependency>path_resolution_python.prompt</pdd-dependency>

Anti-patterns to avoid

Including large swaths of the codebase as context overwhelms the model and inflates costs. Use targeted <include select="..."> tags or example files instead.
When you manually edit generated code without updating the prompt, the prompt and code drift apart. Fix the prompt, then run pdd fix or pdd sync to regenerate.
# Correct: update the prompt, then regenerate
pdd update --git my_module_python.prompt src/my_module.py
pdd --force sync my_module
If a prompt exceeds 30% of the expected code size, it is likely over-specified. Split it into smaller prompts with explicit interfaces using pdd split.
Don’t dictate variable names, loop structures, or class hierarchies. Write requirements at the level of architecture, contract, and intent. Let the model fill in routine implementation.
Tests are permanent assets. Never overwrite the test file. PDD accumulates tests over time, creating a ratchet effect where each new test permanently constrains future generations.

Checklist before running pdd generate

Required:
  • Module purpose is clear (1–2 sentences)
  • Requirements are testable and behavioral (5–10 items)
  • Dependencies included via <include> if external or critical
For established modules:
  • Tests exist for known edge cases
  • Previous generation was successful (grounding will use it)
You do not need to specify:
  • Coding style → shared preamble handles this
  • Implementation patterns → grounding handles this
  • Every edge case → accumulated tests handle this

Build docs developers (and LLMs) love