PyFr¶
A cookiecutter template for production-ready Python microservices.
You answer a short list of prompts and receive a repository that runs, is tested, is observable, and is documented. The target is a working service in ten minutes, in place of the two to four weeks teams spend assembling the same components by hand.
A cookiecutter template is a project skeleton with fill-in-the-blank placeholders. The cookiecutter tool asks you questions, substitutes your answers into the skeleton, and writes a new repository to disk.
Status: M0–M8 done
The template is usable. {{cookiecutter.project_slug}}/ is the
template body and the source of truth: the reference service —
examples/reference-service/
— is rendered from it and never edited by hand.
M7, the conversion into a template, is complete. The template renders
from twelve prompts; it prunes the backends you do not choose; a
generated project carries its own continuous-integration, nightly,
release and documentation workflows, ships its own documentation site
about itself, and records the template version it came from; and on
every merge to main and every night, three combinations are
generated for real and run through their own just check-all. M8,
template updates, is complete too: just update pulls a later
template version into a generated project through an ordinary git
merge, and a weekly workflow in every generated project opens the
pull request for it.
PyFr is built in three phases. Phase A (milestones M0 to M6) built that service as ordinary Python, with no template placeholders anywhere. Phase B (M7) converted it into the template. Phase C (M8, then permanently) keeps the two in step forever after. The reason is stated plainly in the design: never debug Jinja and Python at the same time. (Jinja is the placeholder language cookiecutter uses.)
See the roadmap for what each milestone delivered.
You can generate a project today — Getting started — and you can read the reference service, run it, and copy from it. Everything on this site describes code that exists and runs today.
What you own¶
Generated code belongs entirely to the team that generated it. A generated
service imports no PyFr package: there is no framework to upgrade and no
library that can break you. The one thing PyFr publishes is the updater,
pyfr-cli, which runs at update time and is never imported.
That choice has an obvious cost — a fix in PyFr does not reach services already generated — and M8 removed it: a generated project pulls later template versions into itself through a normal git merge. Teams keep full ownership and receive fixes. The reasoning is in Why a template, not a framework.
What a generated service contains¶
Everything below already runs in the reference service, today:
- FastAPI with an application factory and a lifespan, so tests build the app themselves instead of importing a module-level global.
- Four layers — domain, services, infrastructure, api — with the dependency rule enforced by a build check, not by code review.
- Configuration validated at startup. A bad value stops the process with a readable message instead of causing an error an hour later.
- Structured logging: one JSON object per line, on standard output, with every third-party library's records passing through the same chain.
- Three health endpoints answering three different questions — see HTTP API.
- RFC 9457 Problem Details error responses. RFC 9457 is the internet standard shape for a JSON error body.
- Correlation identifiers: one identifier binds every log line a single request produced.
- Graceful shutdown, so a rolling deployment does not drop live requests.
- A hardened container image: non-root user, no build tools, no shell utilities in the final layer.
- Its own
.github/: continuous integration, a nightly run, releases numbered by Commitizen from Conventional Commits, a Dependabot schedule, and adocs.ymlthat deploys the documentation site below to GitHub Pages — all running from the first push.
The documentation a generated service ships¶
Every generated project gets a documentation site about itself: a getting
started guide, a runbook, guides, reference pages, its decision records and
a glossary, built with MkDocs from its own docs/ directory and deployed by
its own docs.yml workflow.
The reference service's site is that site, rendered from the template with the reference answers and deployed together with this one. It is the worked example: what it says about the reference service, a generated project's site says about that project — with the pages, sections and terms of the backends it did not choose pruned away.
This site, by contrast, is about PyFr itself: how to generate a project, how the template is built and tested, and why it is a template rather than a framework.
Where to go next¶
| If you want to | Read |
|---|---|
| Generate a project | Getting started |
| Run the reference service and place an order | Getting started on the reference service's site |
| Add your own endpoint through all four layers | Add an endpoint |
| Store data in something PyFr does not ship | Add a backend |
Look up a just command |
Commands |
| Look up an environment variable | Configuration |
| Look up an endpoint or an error shape | HTTP API · Errors |
| Understand how the pieces fit | Architecture |
| Know what ships when | Roadmap |
| Work on PyFr itself | Contributing |
| Look up a term used on this site | Glossary |