This boilerplate repository can be used to kickstart Python projects and demonstrates modern tools and practices for building reliable, maintainable, and high‑quality code.
We use UV as the Python package and environment manager.
uv is a fast replacement for pip/virtualenv/poetry that:
- Manages dependencies
- Creates virtual environments
- Reproducibly installs packages via a lockfile
It ensure consistent environments across machines. Use uv.lock to reproduce the exact same setup anywhere.
- Fast dependency resolution
- Reproducible environments (
uv sync) - Works well in CI pipelines
# install Python
uv python install 3.11
# install dependencies
uv sync --dev
# add dependencies
uv add pandas
# run commands inside the environment
uv run pytest
uv run ruff check .More details available on the UV website: https://docs.astral.sh/uv/
To ensure code quality, we enforce automated checks that validate style, detect bugs early, and improve maintainability.
- Ruff → linting + formatting
- Mypy → static type checking
Formatting ensures your code follows a consistent style automatically, without manual effort.
Example:
# before
def add(a,b):return a+b
# after
def add(a, b):
return a + b- No debates about code style
- Cleaner diffs in pull requests
- Easier collaboration
# format code
uv run ruff format .
# check formatting without modifying files
uv run ruff format --check .Linting analyzes your code for:
- Bugs
- Bad practices
- Style issues
It does static analysis without running the code.
import math # ❌ unused import
def compute(x):
if x == None: # ❌ bad practice
return 0
return xRuff will flag:
- Unused imports
- Incorrect comparisons (
== Noneinstead ofis None) - Undefined variables
- Complexity issues
- Prevents common bugs early
- Enforces best practices
- Keeps the codebase clean
# check for issues
uv run ruff check .
# automatically fix issues when possible
uv run ruff check . --fixType hints explicitly define the expected types of variables, inputs, and outputs in your code.
Example:
def add(a: int, b: int) -> int:
return a + bThis means:
aandbmust be integers- the function returns an integer
Mypy checks that your code respects these type definitions without running it.
Example:
def add(a: int, b: int) -> int:
return a + b
add(1, "hello") # ❌ error detected by mypyMypy will raise an error because "hello" is not an int.
- Catches bugs before runtime
- Acts as documentation
- Improves IDE autocompletion
- Makes large codebases easier to understand
# run type checking
uv run mypy .| Tool | Purpose | When it helps |
|---|---|---|
| Ruff (lint) | Detect bugs & bad patterns | Before running code |
| Ruff (fmt) | Enforce style | Always |
| Mypy | Validate types | During development |
- Formatting → makes code consistent automatically
- Linting → detects bugs and bad practices early
- Type hints → make code safer and self-documented
We use MkDocs for documentation.
- Generates a static documentation website
- Automatically deployed via CI/CD
The comments left under commands using docstrings is used to create a documentation website. As such, functions should be described using the following format:
def calculate_average(values: list[float]) -> float:
"""
*function explanation and description*
Args:
values: A non-empty list of floating-point numbers.
Returns:
The arithmetic mean of the input values.
Raises:
ValueError: If the input list is empty.
Examples:
>>> calculate_average([1.0, 2.0, 3.0])
2.0
"""
if not values:
raise ValueError("values must not be empty")
return sum(values) / len(values)Docs are deployed to GitHub Pages after merge to main.
- name: Deploy docs to GitHub Pages
run: uv run mkdocs gh-deploy --forceDocumentation:
- Improves onboarding
- Serves as living reference
- Acts as implicit specification of behavior
We use pytest for automated testing.
- Unit tests → small isolated functions
- Integration tests → components working together
- End-to-end tests → full system validation
- Prevents regressions
- Ensures correctness
- Improves confidence in code changes
uv run pytestTo check whether your code is well tested, you can check how well your code is "covered" by tests.
Running uv run pytest --cov . will run the test coverage program. It will tell you if your code is well covered
➜ python-boilerplate git:(main) uv run pytest --cov .
================================================================= test session starts =================================================================
platform darwin -- Python 3.14.4, pytest-9.0.2, pluggy-1.6.0
rootdir: /Users/pcayetan/dev/python-boilerplate
configfile: pyproject.toml
testpaths: tests
plugins: cov-7.1.0
collected 1 item
tests/test_main.py . [100%]
=================================================================== tests coverage ====================================================================
__________________________________________________ coverage: platform darwin, python 3.14.4-final-0 ___________________________________________________
Name Stmts Miss Cover Missing
--------------------------------------------------------------
python_boilerplate/__init__.py 2 0 100%
python_boilerplate/main.py 6 3 50% 35-38
tests/test_main.py 3 0 100%
--------------------------------------------------------------
TOTAL 11 3 73%
Required test coverage of 50% reached. Total coverage: 72.73%
================================================================== 1 passed in 0.17s ==================================================================This report in HTML format details which specific functions or lines are not covered. To create it and open it in your web browser, run the following command:
uv run coverage html && open htmlcov/index.html
Pre-commit automatically runs checks before code is committed.
- Linting (Ruff)
- Formatting
- Type checking (Mypy)
- Tests (pytest)
- Prevents low-quality code from entering the repository
- Reduces manual errors
- Automates quality checks early
pre-commit install
pre-commit run --all-filesThe pre-commit hooks and the scripts run in that hook are configured in the .pre-commit-config.yaml file.
We use GitHub Actions to implement Continuous Integration and Delivery.
-
CI (Continuous Integration)
Every change:- is automatically tested
- validated through quality checks
- ensures it does not break the codebase
-
CD (Continuous Deployment/Delivery)
After passing CI:- code is packaged
- deployed automatically
- releases become predictable and repeatable
The CI job runs on every push and pull request:
- Lint → ruff check
- Format → ruff format --check
- Type check → mypy
- Tests → pytestThese are quality gates:
- Code must pass all checks before merging
- Keeps the
mainbranch always healthy
The docs-deploy job:
- Runs only on
mainbranch - Builds and deploys documentation
if: github.ref == 'refs/heads/main'- No manual deployment
- Always up-to-date documentation
- Reliable release process
- Create branches for:
- Features (
feature/...) - Fixes (
fix/...)
- Features (
- Open a Pull Request
- Run CI before merging
- Create a branch
git checkout -b feature/my-feature-
Develop code
-
Pre-commit runs locally (fast feedback)
-
Push branch → open Pull Request
-
CI runs automatically:
- Lint ✅
- Format ✅
- Types ✅
- Tests ✅
-
Code review
-
Merge into
main
- Automation → reduces manual errors
- Collaboration → enforced code reviews
- Continuous validation → prevents regressions
- Quality gates → only clean code reaches main
This boilerplate demonstrates a modern Python workflow:
- uv → dependency management
- Ruff + Mypy → code quality
- pytest → testing
- pre-commit → local quality enforcement
- GitHub Actions → CI/CD automation
Together, they implement DevOps best practices:
- Faster development cycles
- Higher code quality
- Reliable and repeatable deployments