
こんにちは!システム開発部でエンジニアをやっている春名です。
この記事はABEJAアドベントカレンダー2025の19日目の記事です。
Pythonの開発環境については、過去に以下のような記事を書きました。
ユーモアあふれる記事が並ぶ中ではやや地味なテーマですが、この記事の初公開から早くも2年が経ちました。 その間に環境もいくつか変化しているため、改めて現在システム開発部で利用している開発環境をご紹介します!
前回からの大きな変化点で言うと以下になります。
- パッケージマネージャがpoetryに変わりuvが主流になった
- ruffにformatterが取り込まれblackを個別に使う必要がなくなった
それに加えてシステム開発部では、これらの変更を取り込んだPythonのバックエンド開発環境を cookiecutterで利用できるように整備しています。
これによりプロジェクト開始時のバックエンドの構築が爆速で終わるようになっています🚀
目次
- 目次
- 今回作成する環境
- cookiecutter のテンプレート作成
- uvによるプロジェクトの作成
- cookiecutterでテンプレートを使う
- cookiecutterのHooks機能
- おわりに
- We are hiring!
今回作成する環境
前回と同様にFastAPIを利用してAPIを提供するバックエンド環境を作成する場合を例にしてご紹介します。
以下のツールを導入し、その環境を cookiecutterのテンプレートとして使えるように作成していきます。
リンターとフォーマッターがRuffに統合されたので、だいぶスッキリしましたね。
あとは今の構成だとmypyによる型チェックに時間がかかっているので、移行先としてtyに注目しています。
まだalpha版ですが、uvやruffも開発しているAstralが開発する型チェッカーなので、相性良く使えそうな気がしています。
使用しているPC環境は以下の通りです。
- MacBook Pro 2022 (M2)
- Python 3.11
cookiecutter のテンプレート作成
まずはcookiecutterのテンプレート作成から行います。
cookiecutterのインストール
公式に記載されているpipxやpipからのインストールや、Macの場合はbrewを使ってもインストールできます。
pipx install cookiecutter or brew install cookiecutter
テンプレートディレクトリの作成
ここでは {{cookiecutter.project_slug}}というディレクトリを作成し、テンプレートタグでプロジェクトのスラッグ名が指定できるようにしておきます。
mkdir -p fastapi-backend-template/{{cookiecutter.project_slug}}
cookiecutter.jsonの作成
作成したディレクトリのルートに cookiecutter.json というファイルを作成します。
cd fastapi-backend-template touch cookiecutter.json
cookiecutter.jsonにはテンプレートとして使用するタグの内容を記述します。
{ "project_name": "FastAPI Backend", "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '-') }}" }
project_slugにはプロジェクト名のスペースをハイフンに変換したものがデフォルトで入力されるようにしています。
uvによるプロジェクトの作成
uvのインストール
公式のインストール手順に従いインストールします。
$ curl -LsSf https://astral.sh/uv/install.sh | sh $ uv --version uv 0.9.15 (5eafae332 2025-12-02)
プロジェクトの作成
プロジェクトスラッグ名のディレクトリ内にuvでプロジェクトを作成します。
$ cd {{cookiecutter.project_slug}} $ uv init backend $ cd backend $ tree . backend ├── README.md ├── main.py └── pyproject.toml 1 directory, 3 files
依存関係の追加
この辺りはパッケージマネージャーがuvに変わっただけで、やっていることは前回と同じです。
FastAPI
uv install "fastapi[all]"
Ruff / mypy / pytest
uv add ruff mypy pytest --dev
hadolint
brew install hadolint
Hadolintの設定も特に変わっていません。
.hadolint.yaml
ignored: - DL3008 # Pin versions in apt get install
pyptoject.tomlの更新
pyptoject.tomlへRuffやmypyなどの設定を行なっていきます。
name: "{{cookiecutter.project_slug}}"という記述にすることで、作成したプロジェクトのスラッグ名が反映されるようにしています。
あとはRuffの設定記述方法が少し変わっていたり、black個別の設定がなくなっていたりしますが基本的には前回と同じ内容です。
[project] name = "{{cookiecutter.project_slug}}" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.11" dependencies = [ "fastapi[all]>=0.123.9", ] [project.optional-dependencies] dev = [ "mypy>=1.19.0", "ruff>=0.14.8", ] test = [ "pytest>=9.0.1", ] [tool.ruff] target-version = "py311" line-length = 100 [tool.ruff.lint] select = [ "E", # pycodestyle errors "W", # pycodestyle warnings "F", # pyflakes "B", # flake8-bugbear "I", # isort ] ignore = [ "E501", # line too long, handled by black "B008", # do not perform function calls in argument defaults ] unfixable = [ "F401", # module imported but unused "F841", # local variable is assigned to but never used ] [tool.mypy] python_version = "3.11" strict = true [tool.uv] package = false
FastAPIの処理を実装
あとはテンプレートにしたいFastAPIの処理を実装をしていきます。
システム開発部のテンプレートでは、docker-composeによるpostgresの起動や、SQLAlchemyを使用したユーザーに対するCRUD、almebicによるDBマイグレーションなど、殆どのプロジェクトで使用するであろう構成をテンプレートとして実装してあります。
ここでは、シンプルなヘルスチェックのエンドポイントのみを持ったコードを例として記載します。
rm main.py mkdir -p src/routers src/schemas touch src/__init__.py touch src/main.py touch src/schemas/health_check.py touch src/routers/health_check.py
src/main.py
from fastapi import FastAPI from src.routers import health_check app = FastAPI() app.include_router(health_check.router)
routers/health_check.py
from fastapi import APIRouter from src.schemas.health_check import HealthCheck router = APIRouter() @router.get("/health-check", response_model=HealthCheck) def health_check() -> HealthCheck: return HealthCheck(status="ok")
schemas/health_check.py
from pydantic import BaseModel class HealthCheck(BaseModel): status: str
実装が完了したら以下で起動して動作確認ができます。
uv run uvicorn src.main:app --host 0.0.0.0 --reload
$ curl http://127.0.0.1:8000/health-check
{"status":"ok"}
単体テストの実装
pytestでテストするコードの実装です。
テストしている内容は前回と同じですが、テストケースを追加しやすいようクラスにまとめたり@pytest.mark.parametrizeなどを使うことで拡張しやすい形でテンプレートには実装してあります。
mkdir -p tests/unit/routers touch tests/__init__.py touch tests/unit/__init__.py touch tests/unit/routers/__init__.py touch tests/unit/routers/conftest.py touch tests/unit/routers/test_health_check.py
tests/unit/routers/conftest.py
from typing import Generator import pytest from fastapi.testclient import TestClient from src.main import app @pytest.fixture def client() -> Generator[TestClient, None, None]: # NOTE: Add overrides to app here if needed. with TestClient(app, raise_server_exceptions=False) as client: yield client
tests/unit/test_health_check.py
import pytest from fastapi.testclient import TestClient class TestGetHealthCheck: @pytest.mark.parametrize( "expected_status_code, expected_response", [ (200, {"status": "ok"}), ], ) def test_valid( self, client: TestClient, expected_status_code: int, expected_response: dict[str, str], ) -> None: response = client.get("/health-check") assert response.status_code == expected_status_code assert response.json() == expected_response
単体テスト実行
pytestコマンドで実行し、PASSEDになることを確認します。
$ uv run pytest tests/ ==================================== test session starts ==================================== platform darwin -- Python 3.11.8, pytest-9.0.1, pluggy-1.6.0 -- /Users/user/fastapi-backend-template/{{cookiecutter.project_slug}}/backend/.venv/bin/python3 cachedir: .pytest_cache rootdir: /Users/user/fastapi-backend-template/{{cookiecutter.project_slug}}/backend configfile: pyproject.toml plugins: anyio-4.12.0 collected 1 item tests/unit/routers/test_health_check.py::TestGetHealthCheck::test_valid[200-expected_response0] PASSED [100%] ===================================== 1 passed in 0.27s =====================================
これでテンプレートなるFastAPI環境の構築は完了です。
確認が終わったらテンプレートには不要なファイルは削除しておきましょう。
rm -fr .venv find . -name '__pycache__' -type d -exec rm -r {} +
cookiecutterでテンプレートを使う
作成したテンプレートをcookiecutterで使ってみます。
使用する際はcookiecutterで作成したテンプレートのディレクトリを指定するだけです。
$ cookiecutter fastapi-backend-template [1/2] project_name (FastAPI Backend): DX Project [2/2] project_slug (dx-project):
実行すると設定したタグに対するプロンプトが表示されるので、それらを入力するとプロジェクトディレクトリが作成されます。
$ tree dx-project -d
dx-project
└── backend
├── src
│ ├── routers
│ └── schemas
└── tests
└── unit
└── routers
できました!
あとはこのテンプレートをリポジトリに登録しておけば、以下のように利用可能です。
cookiecutter git@github.com:your-org/fastapi-backend-template or cookiecutter https://github.com/your-org/fastapi-backend-template
cookiecutterのHooks機能
cookiecutterにはテンプレートからプロジェクトを作る特定のタイミングで任意の処理を実行するHooks機能があります。
テンプレートディレクトリのルートに hooksというディレクトリを作成し、以下のファイル名を持つスクリプトを配置することで実行されます。
今回作成したテンプレートに配置する場合は以下のような形です。(.shも配置可能ですが公式では.pyを推奨)
fastapi-backend-template/ ├── {{cookiecutter.project_slug}}/ ├── hooks │ ├── pre_prompt.py │ ├── pre_gen_project.py │ └── post_gen_project.py └── cookiecutter.json
それぞれ3つのファイル名が意味するタイミングで、任意の処理を実行させることができます。 詳細は公式ドキュメントをご参照ください。
システム開発部では、この機能を利用してフロントエンドとバックエンドをモノレポで管理するテンプレートも作成しています。
- プロジェクト全体を管理するテンプレート
- フロントエンドを管理するテンプレート
- バックエンドを管理するテンプレート
これらのテンプレートをcookiecutterで作成し、1のテンプレートを実行してプロジェクトを作った際、post_gen_project.pyで残りの2、3のテンプレートを使ったプロジェクトを配下に作る、といった形です。
一部の抜粋になりますが、post_gen_project.py内でcookiecutterを使うことで複数のテンプレートを同時に展開しています。
#!/usr/bin/env python import os import subprocess from pathlib import Path from cookiecutter.main import cookiecutter import shutil PROJECT_DIR = Path(os.getcwd()) BACKEND_DIR = PROJECT_DIR / "backend" cookiecutter( "git@github.com:your-org/fastapi-backend-template.git", checkout="main", no_input=True, output_dir=str(BACKEND_DIR.parent), extra_context={ "project_slug": "{{ cookiecutter.project_slug }}", }, ) # backend/をプロジェクトのルートに移動 for item in (PROJECT_DIR / "{{ cookiecutter.project_slug }}" / "backend").iterdir(): shutil.move(item, BACKEND_DIR / item.name) # 不要になったプロジェクトディレクトリを削除 shutil.rmtree(PROJECT_DIR / "{{ cookiecutter.project_slug }}", ignore_errors=True)
このようにhooksを活用することで、それぞれのテンプレートを独立したリポジトリでメンテしつつ、利用する際は1度の実行で環境が整うようになっています!
おわりに
テンプレート化して再利用できるようにすることで、プロジェクト開始時の環境構築のコストはかなり下がったように感じます。
このテンプレートでAGENTS.mdなども管理し、AI駆動開発に対応しつつ運用中です。
再利用できるものは積極的にメンバー全員が協力して再利用できるような形に整備し、そこで節約できた時間をよりミッションクリティカルな部分の開発に注力することに使っていけると良いなと思っています。
We are hiring!
今回ご紹介したような開発効率化のための環境整備なども含め、一緒にテクノロジーの社会実装に取り組んでいただけるエンジニアを募集中です!
ソフトウェアエンジニアポジションはこちらです。
新卒の方もお待ちしております!
