Universal Templating Skill
Comprehensive guide to template format selection, design patterns, generation workflows, and best practices across Handlebars, Cookiecutter, Copier, Maven, and Harness templates.
Template Format Matrix
Handlebars
Use Cases:
-
Simple variable substitution
-
Email templates
-
Document generation
-
Configuration files
-
Quick string templating
Syntax:
Hello {{name}}, {{#if premium}}Welcome to premium!{{/if}} {{#each items}}- {{this}}{{/each}}
Strengths:
-
Minimal learning curve
-
Fast execution
-
Great for config files
-
Small file size
-
No external dependencies
Weaknesses:
-
Limited logic capabilities
-
No native loops/conditionals
-
Requires helpers for complex operations
Best For: Configuration file templating, simple document generation
Cookiecutter
Use Cases:
-
Interactive project scaffolding
-
Multi-step wizard templates
-
Python package templates
-
Post-generation hooks
Syntax:
{ "project_name": "{{ cookiecutter.project_name }}", "author": "{{ cookiecutter.author_name }}" }
Strengths:
-
Interactive CLI prompts
-
Python ecosystem integration
-
Post-generation hooks
-
Conditional rendering
-
JSON-based config
Weaknesses:
-
Python dependency required
-
Jinja2 templates (verbose)
-
Less flexible validation
-
Community templates vary in quality
Best For: Python projects, quick prototypes, community templates
Copier
Use Cases:
-
Modern project scaffolding
-
Template versioning and updates
-
Multi-template composition
-
Complex validation rules
Syntax:
_templates_suffix: .jinja _copy_without_render:
- "*.png"
- "*.jpg"
project_name: type: str help: What is your project name? default: my_project
Strengths:
-
Powerful Jinja2 templating
-
Template versioning
-
Update existing projects
-
Composite templates
-
Advanced validation
-
Excellent documentation
Weaknesses:
-
Python dependency
-
Steeper learning curve
-
Larger footprint
-
Development active (API changes possible)
Best For: Enterprise templates, versioned scaffolding, complex projects
Maven
Use Cases:
-
Java/JVM project archetypes
-
Enterprise Java scaffolding
-
Build system integration
-
Dependency management
Syntax:
<archetype> <groupId>org.apache.maven.archetypes</groupId> <artifactId>maven-archetype-quickstart</artifactId> </archetype>
Strengths:
-
Native Maven integration
-
Build tool awareness
-
Dependency management
-
Enterprise adoption
-
IDEs have built-in support
Weaknesses:
-
Java/JVM only
-
XML-heavy
-
Complex archetype internals
-
Verbose setup
Best For: Java/JVM projects, Maven-based builds
Harness Templates
Use Cases:
-
CI/CD pipeline steps
-
Reusable stage definitions
-
Pipeline patterns
-
Deployment strategies
Syntax:
template: name: Deploy Service type: StepGroup spec: steps: - step: name: Deploy K8s identifier: deploy_k8s type: K8sDeploy spec: service: <+input>
Strengths:
-
Native Harness integration
-
Expression language support
-
Runtime inputs
-
Pipeline-aware
-
Built-in approval flows
Weaknesses:
-
Harness-specific only
-
YAML complexity
-
Requires Harness setup
-
Limited reusability outside Harness
Best For: Harness pipelines, deployment templates
Format Selection Decision Tree
START: Need to generate what? │ ├─ Configuration files │ ├─ Simple substitution → Handlebars │ └─ Complex validation → Copier │ ├─ Project scaffold │ ├─ Python project → Cookiecutter │ ├─ Enterprise/versioned → Copier │ └─ Java/JVM → Maven │ ├─ CI/CD pipeline │ ├─ Harness platform → Harness Templates │ └─ Other CI → Handlebars + custom │ ├─ Document/email │ └─ Handlebars │ └─ Reusable components ├─ Code snippets → Handlebars └─ Full modules → Copier
Generation Workflow Steps
Step 1: Template Planning
Inputs:
-
Target audience (users, developers, automation)
-
Use cases and scenarios
-
Complexity level (simple, moderate, advanced)
-
Maintenance burden tolerance
-
Integration requirements
Deliverables:
-
Template specification document
-
Format selection justification
-
Variable naming convention document
-
Example instantiation
Questions to Answer:
-
What will be generated?
-
Who uses it (users, scripts, tools)?
-
How often will it change?
-
Will it need versioning?
-
What validation is needed?
Step 2: Variable Definition
Essential Variables:
project_name - Primary identifier author_name - Creator/maintainer organization - Company/org name description - Brief description license - License type (MIT, Apache, etc.) target_framework - Framework/language version
Optional Variables (by use case):
// Python projects python_version - Target Python version package_name - PyPI package name django_version - Django version (if applicable)
// Java projects java_version - JDK version groupId - Maven group ID artifactId - Maven artifact ID
// Cloud projects aws_region - AWS region kubernetes_cluster - K8s cluster name docker_registry - Container registry
Step 3: Variable Naming Conventions
Naming Rules:
Format: snake_case (all formats support this)
Prefixes:
-
generated_*
-
Files/content created by template
-
input_*
-
User input required
-
computed_*
-
Derived from other variables
-
optional_*
-
Optional user input
Examples:
✓ project_name ✓ author_email ✓ generated_version ✓ target_framework ✗ ProjectName (avoid PascalCase) ✗ PROJECT_NAME (avoid SCREAMING_SNAKE_CASE)
Step 4: Content Structure Design
Standard Project Structure:
{project_name}/ ├── README.md # Template instructions ├── {project_name}/ # Main package/app │ ├── init.py # (if applicable) │ ├── main.py │ └── config.py ├── tests/ # Test directory │ ├── init.py │ └── test_main.py ├── docs/ # Documentation │ └── API.md ├── .gitignore ├── LICENSE ├── requirements.txt # (Python) ├── setup.py # (Python) ├── package.json # (Node.js) └── {{cookiecutter.var}}/ # Template variables
Step 5: Conditional Rendering
When to Use:
-
Optional features
-
Different project types
-
Target-specific configurations
-
License-based files
Handlebars Example:
{{#if include_docker}} FROM python:3.11 COPY . /app {{/if}}
Cookiecutter/Copier Example:
{%- if use_docker %}
Docker configuration
{%- endif %}
Step 6: Validation & Constraints
Input Validation:
-
Email format checking
-
Version number validation
-
Project name uniqueness checks
-
Path validation
Copier Example:
project_name: type: str help: Project name (lowercase, alphanumeric + underscore) regex: "^[a-z_][a-z0-9_]*$"
python_version: type: str default: "3.11" help: Python version (3.9, 3.10, 3.11, 3.12) choices: - "3.9" - "3.10" - "3.11" - "3.12"
Step 7: Post-Generation Hooks
Cookiecutter/Copier Hooks:
hooks/post_gen_project.py
import os from pathlib import Path
Initialize git repository
os.system("git init")
Create virtual environment
os.system("python -m venv venv")
Install dependencies
os.system("pip install -r requirements.txt")
Generate API docs
os.system("python generate_docs.py")
Step 8: Documentation
Required Documentation:
-
README.md - How to use template
-
VARIABLES.md - All available variables
-
EXAMPLES.md - Example instantiations
-
TROUBLESHOOTING.md - Common issues
Best Practices for Template Design
- Variable Defaults
Good Defaults:
Clear, sensible defaults
author_name: "Your Name" license: "MIT" python_version: "3.11" # Latest stable include_docker: false # Opt-in for complexity include_tests: true # Always good to start with tests
Bad Defaults:
Unclear or empty
author_name: "" unknown_var: "???" version: "1.0.0" # Should be context-aware
- DRY Principle (Don't Repeat Yourself)
Template Variables Once:
Define: project_name = "my_project" Use in:
- Directory name
- README title
- Setup.py name
- Docker image name
- File Organization
Group Related Files:
template/ ├── [project_name]/ # Project source (stays as-is) ├── [project_name]_docs/ # Docs structure ├── [project_name]_config/ # Config templates └── tests/ # Test templates
- Template Readability
Use Clear Comments:
{# This file is generated from {{template_name}} #} {# Last updated: {{generated_date}} #} {# For questions, see: {{docs_url}} #}
- Version Management
Template Versioning:
In template metadata
version: "1.0.0" harness_compatibility: "1.4+" minimum_python: "3.9"
- Error Handling
Clear Error Messages:
Instead of: ValueError
Use:
if not re.match(r"^[a-z_][a-z0-9_]*$", project_name): raise ValueError( f"Project name '{project_name}' is invalid.\n" f"Must start with lowercase letter or underscore,\n" f"followed by lowercase letters, numbers, or underscores." )
Template Generation Workflow
End-to-End Generation Process
-
USER SELECTION ├─ Choose template ├─ Select format (if flexible) └─ Provide variables
-
VALIDATION ├─ Validate all inputs ├─ Check constraints └─ Generate variable report
-
PRE-PROCESSING ├─ Compute derived variables ├─ Expand conditionals └─ Build file tree
-
GENERATION ├─ Render templates ├─ Copy static files ├─ Create directory structure └─ Handle special files
-
POST-PROCESSING ├─ Run hooks ├─ Initialize git/vcs ├─ Install dependencies └─ Generate documentation
-
VALIDATION ├─ Check generated files ├─ Verify structure ├─ Test basic functionality └─ Generate report
-
OUTPUT ├─ Display summary ├─ Provide next steps └─ Save manifest
Harness Expression Language in Templates
Available Context Variables
Harness Expressions: ├─ <+input.VARIABLE_NAME> # Inputs ├─ <+pipeline.PROPERTY> # Pipeline-level ├─ <+stage.PROPERTY> # Stage-level ├─ <+steps.STEP_ID.PROPERTY> # Step outputs ├─ <+env.PROPERTY> # Environment variables ├─ <+secrets.getValue("NAME")> # Secret references └─ <+execution.PROPERTY> # Execution context
Template Examples with Expressions
template: name: Deploy Service type: Step spec: service: name: <+input.service_name> environment: name: <+input.environment> variables: version: <+input.artifact_version> deploy_timeout: <+input.timeout_minutes> approval_required: <+input.requires_approval>
Related Documentation
-
Handlebars.js
-
Cookiecutter
-
Copier
-
Maven Archetypes
-
Harness Templates