Resume as Code
Oct 2026 · Source
I wanted my portfolio to show how I build data systems, not just describe it. So the site itself is a small pipeline: content is the data, and the website and PDF resume are outputs.
How it works
- Extract: resume, bio, and project pages are plain YAML and Markdown files.
- Validate: every file is checked against strict pydantic models before anything renders. Unknown fields, impossible date ranges, malformed citations, and missing images all fail the build.
- Render: Jinja templates produce the HTML pages, and Typst compiles the same resume data into a PDF. One source of truth means the site and the PDF can't drift apart.
- Deploy: GitHub Actions lints, type-checks, tests, and publishes to GitHub Pages on
every push to
main.
Design choices
- Content as data, not markup. The resume is handed to the Typst template as JSON,
so characters like
#,*, and$in my content render literally with no escaping logic. - A one-page budget, enforced. The PDF build measures its own page count and steps the layout down (100% to 88%) until it fits. If it still doesn't fit, the build fails instead of publishing a two-page resume, and a test enforces the same in CI.
- Reproducible output. The PDF uses only Typst's bundled fonts, so my laptop and CI produce identical files.
- Decisions are written down. Architecture decisions live as ADRs in the repo.
Stack: Python 3.13, Poetry, pydantic, Jinja, Typst, pytest, ruff, mypy (strict), pre-commit, GitHub Actions, GitHub Pages.