Python Copier Project Scaffolding — Core Concepts
Why Copier Exists
Cookiecutter proved that project scaffolding saves time. But it has a gap: once a project is generated, the connection to the template is severed. Copier closes that gap by tracking the template version and user answers inside the generated project, making future updates possible.
Install Copier with pip or pipx:
pipx install copier
Creating a Project
copier copy gh:your-org/python-template my-new-project
Copier asks the questions defined in the template, generates the project, and writes a .copier-answers.yml file inside it. This file records every answer and the exact template commit, creating a link back to the source.
Template Structure
A Copier template is a Git repository with a copier.yml (or copier.yaml) file at the root:
# copier.yml
project_name:
type: str
help: "Human-readable project name"
project_slug:
type: str
default: "{{ project_name | lower | replace(' ', '-') }}"
use_docker:
type: bool
default: true
python_version:
type: str
choices:
- "3.11"
- "3.12"
- "3.13"
default: "3.12"
_subdirectory: "project"
The _subdirectory key tells Copier that the actual template files live in a subfolder, keeping template config separate from template content.
The Update Workflow
This is Copier’s killer feature. When the template evolves:
cd my-new-project
copier update
Copier compares three states:
- Old template — the version recorded in
.copier-answers.yml - New template — the latest (or specified) version
- Your project — including your custom changes
It performs a three-way merge, applying template changes without overwriting your modifications. Conflicts are handled like Git merge conflicts — you resolve them manually.
Conditional Files with Jinja Suffixes
Instead of relying on hooks to delete unwanted files, Copier supports Jinja-powered file names natively:
{{ "Dockerfile" if use_docker }}
{{ ".github" if use_github_actions }}/workflows/ci.yml
When use_docker is false, the Dockerfile simply isn’t created. No cleanup scripts needed.
Migration Tasks
When a template upgrade involves breaking changes — like renaming a folder or changing a config format — Copier supports migration tasks:
# copier.yml
_tasks:
- command: "python scripts/migrate.py"
when: "{{ _copier_conf.old_version < '2.0' }}"
These run automatically during copier update, handling structural changes that a simple file diff cannot.
Common Misconception
“Copier is just Cookiecutter with updates.” While the update feature is the headline, Copier also has cleaner multi-choice handling, native type validation (bool, int, float, not just strings), and built-in support for template versioning through Git tags. The question schema alone makes templates more robust.
One thing to remember: Copier keeps a living connection between your project and its template — updates flow forward instead of forcing you to start over.
See Also
- Python Black Formatter Understand Black Formatter through a practical analogy so your Python decisions become faster and clearer.
- Python Bumpversion Release Change your software's version number in every file at once with a single command — no more find-and-replace mistakes.
- Python Changelog Automation Let your git commits write the changelog so you never forget what changed in a release.
- Python Ci Cd Python Understand CI CD Python through a practical analogy so your Python decisions become faster and clearer.
- Python Cicd Pipelines Use Python CI/CD pipelines to remove setup chaos so Python projects stay predictable for every teammate.