Skip to main content
New: Deck Doctor. Upload your deck, get CPO-level feedback. 7-day free trial.
TemplateFREEā±ļø 15 minutes

Code Generation Feature Specification Template

A structured template for specifying code generation features in developer tools. Covers input formats, output targets, template engines, customization...

By Tim Adair• Last updated 2026-03-05
Code Generation Feature Specification Template preview

Code Generation Feature Specification Template

Free Code Generation Feature Specification Template — open and start using immediately

or use email

Instant access. No spam.

Need a custom version?

Forge AI generates PM documents customized to your product, team, and goals. Get a draft in seconds, then refine with AI chat.

Generate with Forge AI

What This Template Is For

A code generation specification defines what your tool generates, from what inputs, and how users customize the output. Whether you are building an API client generator, a project scaffolder, a schema-to-types converter, or an AI-assisted code tool, the same core questions apply: what goes in, what comes out, and how does the user control the process.

Code generation tools fail when they produce output that does not match the team's coding standards, when they cannot be extended for edge cases, or when the generated code is so opaque that developers refuse to touch it. This template forces you to think through input validation, output formatting, customization hooks, and the boundary between generated and hand-written code.

This template pairs well with the technical spec template for the engineering implementation plan. If you are evaluating whether to build a code generator versus maintaining hand-written code, score both options with the RICE framework. The Technical PM Handbook covers how to gather requirements from engineering teams, and the developer experience glossary entry has principles that apply to any tool developers interact with.


When to Use This Template

Use this template when you are building a tool that produces source code files from structured input (schemas, configs, API definitions, or user prompts). It is equally useful for template-based generators and AI-powered code assistants.

Skip this template for simple string interpolation (e.g., populating a README from package.json). Those can be documented in a script comment.


How to Use This Template

  1. Define the input format and validation rules first. The quality of generated code depends entirely on the quality and structure of the input.
  2. Document every output target (language, framework, file structure) with a concrete example of what the generated code looks like.
  3. Specify the customization model explicitly. Users will always need to override defaults. Define whether customization happens via config files, CLI flags, template overrides, or code annotations.
  4. Write the "do not generate" boundaries clearly. Generated code that mixes with hand-written code needs clear seams so manual edits are preserved on regeneration.
  5. Include a validation and testing strategy. Generated code must compile, pass lint checks, and satisfy type checkers. Define how the generator verifies its own output.

The Template

Generator Overview

FieldDetails
Generator Name[name]
Purpose[What it generates and why]
Input Source[OpenAPI spec, GraphQL schema, database schema, user prompt, etc.]
Output Languages[TypeScript, Python, Go, etc.]
Distribution[npm package, CLI binary, IDE plugin, CI step]
Trigger[Manual CLI command, file watcher, CI pipeline, on-save]

Input Specification

Supported input formats:

FormatVersionParserExample
[Format 1][Version][Library/custom][Link or inline example]
[Format 2][Version][Library/custom][Link or inline example]

Validation rules:

RuleSeverityDescription
[Rule 1]Error[What must be true for input to be valid]
[Rule 2]Error[What must be true for input to be valid]
[Rule 3]Warning[What triggers a non-blocking warning]

Input preprocessing:

  • [Step 1: Normalize/resolve references]
  • [Step 2: Apply defaults for missing fields]
  • [Step 3: Validate against schema]

Output Specification

Output structure:

generated/
ā”œā”€ā”€ [file-1].[ext]       [What this file contains]
ā”œā”€ā”€ [file-2].[ext]       [What this file contains]
ā”œā”€ā”€ [directory]/
│   ā”œā”€ā”€ [file-3].[ext]   [What this file contains]
│   └── [file-4].[ext]   [What this file contains]
└── index.[ext]           [Barrel exports or entry point]

Per-file specification:

FilePurposeRegeneration Behavior
[file-1][Description]Overwrite on every run
[file-2][Description]Generate once, never overwrite
[file-3][Description]Merge (preserve manual edits in marked sections)

Code style rules:

RuleValue
Indentation[spaces/tabs, count]
Naming convention[camelCase, snake_case, PascalCase for types]
Import style[named imports, namespace imports]
Comment style[JSDoc, inline, none]
Line length[max characters]
Trailing comma[yes/no]

Customization Model

Configuration file: [filename] (e.g., codegen.config.yaml)

generator:
  output_dir: [path]
  language: [language]
  overrides:
    [entity-name]:
      rename: [custom-name]
      skip: [true/false]
      custom_type: [type override]
  plugins:
    - [plugin-name]
  templates:
    [template-key]: [path-to-custom-template]

Template override system:

HookDescriptionDefault
[hook-1][What this template controls][Built-in template name]
[hook-2][What this template controls][Built-in template name]

Custom plugins:

  • [How to write a plugin]
  • [Plugin lifecycle hooks: beforeGenerate, afterGenerate, transformOutput]
  • [Plugin API surface]

Regeneration and Manual Edits

Protected regions:

// @generated-start
[Generated code that will be overwritten]
// @generated-end

// @custom-start
[Hand-written code preserved on regeneration]
// @custom-end

Lock file. [Does the generator produce a lock file tracking generated files?]

Diff behavior. [What happens when input changes? Full regeneration or incremental?]


Validation and Testing

CheckToolWhen
Type checking[tsc, mypy, go vet]After every generation
Lint[eslint, ruff, golangci-lint]After every generation
Compile[Language compiler]After every generation
Snapshot tests[jest, pytest]CI pipeline
Round-trip test[Custom]CI pipeline

Filled Example: API Client Generator (apigen)

Generator Overview

FieldDetails
Generator Nameapigen
PurposeGenerate typed API clients from OpenAPI 3.1 specifications
Input SourceOpenAPI 3.1 YAML or JSON files
Output LanguagesTypeScript (v1), Python (planned v2)
Distributionnpm package (@acme/apigen)
TriggerCLI command (apigen generate) or file watcher (apigen watch)

Input Specification

Supported input formats:

FormatVersionParserExample
OpenAPI3.0, 3.1@apidevtools/swagger-parseropenapi.yaml
Swagger2.0Auto-converted to OpenAPI 3.1swagger.json

Validation rules:

RuleSeverityDescription
Valid OpenAPI schemaErrorInput must pass OpenAPI spec validation
All $ref targets resolveErrorNo broken schema references
operationId on every endpointErrorRequired for function naming
No duplicate operationIdsErrorEach operation must have a unique ID
Response schema definedWarningEndpoints without response schemas generate unknown return types

Output Specification

Output structure:

generated/api/
ā”œā”€ā”€ client.ts            Base client class with fetch configuration
ā”œā”€ā”€ types.ts             All request/response type definitions
ā”œā”€ā”€ endpoints/
│   ā”œā”€ā”€ users.ts         Users endpoint methods
│   ā”œā”€ā”€ projects.ts      Projects endpoint methods
│   └── billing.ts       Billing endpoint methods
└── index.ts             Barrel exports

Generated client usage:

import { ApiClient } from './generated/api';

const client = new ApiClient({
  baseUrl: 'https://api.acme.com',
  token: process.env.API_TOKEN,
});

// Fully typed: params, body, and response
const user = await client.users.getById({ userId: '123' });
// user is typed as UserResponse

Configuration

# apigen.config.yaml
input: ./specs/openapi.yaml
output:
  dir: ./src/generated/api
  language: typescript
overrides:
  User:
    rename: AppUser
  AdminEndpoints:
    skip: true
options:
  runtime: fetch
  date_library: dayjs
  enum_style: union
  optional_nullable: true

Regeneration

All files in generated/api/ are fully regenerated on every run. The generator writes a .apigen-lock.json tracking which files it manages. Running apigen generate deletes all tracked files before regenerating to prevent stale artifacts.

Manual code belongs outside the generated/ directory. Wrapper functions, custom hooks, and business logic should import from the generated client but live in separate files.


Key Takeaways

  • Define input validation rules before output formats. Bad input produces bad generated code regardless of template quality
  • Support at least two output formats (human-readable and machine-parseable) for CLI-based generators
  • Document regeneration behavior explicitly. Users need to know which files are safe to edit and which will be overwritten
  • Include a customization model (config file, template overrides, or plugins) from day one. Every team has naming conventions the defaults will not match
  • Validate generated output with the same linters and type checkers the team uses on hand-written code
  • Write snapshot tests that catch unintended changes in generated output across generator upgrades

About This Template

Created by: Tim Adair

Last Updated: 3/5/2026

Version: 1.0.0

License: Free for personal and commercial use

Frequently Asked Questions

Should generated code be committed to version control?+
Yes. Committing generated code makes builds reproducible without requiring every developer to run the generator locally. It also makes code review possible since reviewers can see what changed in the generated output. Add a CI check that runs the generator and fails if the committed output differs from a fresh generation.
How do I handle breaking changes in the input schema?+
Version your output format independently from the input format. When the input schema introduces a breaking change (renamed field, removed endpoint), the generator should produce a clear error listing what changed and what manual updates are needed. Never silently generate code that compiles but behaves differently.
Should generated code include comments explaining what it does?+
Include a file-level comment stating that the file is auto-generated, with the generator name, version, and timestamp. Do not add inline comments explaining generated logic. The code itself should be readable. If it is not, improve the templates rather than adding comments. The [definition of done template](/templates/definition-of-done-template) can help define quality standards for generated output.
How do I support multiple output languages?+
Separate the parsing and transformation logic from the rendering layer. Parse the input into an intermediate representation (IR), then pass the IR to language-specific renderers. This lets you add a new language by writing a new renderer without touching the parser. Test each language renderer independently with the same set of input fixtures.
When should I use templates versus AST manipulation?+
Use templates (Handlebars, EJS, Jinja) when the output is mostly static text with variable interpolation. Use AST manipulation (ts-morph, libcst, go/ast) when you need to modify existing code files or generate code that requires precise formatting. For most generators, templates are simpler and sufficient. Switch to AST tools only when you need to merge generated code into existing files. ---

Explore More Templates

Browse our full library of PM templates, or generate a custom version with AI.

Free PDF

Like This Template?

Subscribe to get new templates, frameworks, and PM strategies delivered to your inbox.

or use email

Join 10,000+ product leaders. Instant PDF download.

Want full SaaS idea playbooks with market research?

Explore Ideas Pro →