Before you write a line of code — setting up a quality-first Python project with Claude Code
Seven files, seven prompts, zero application code. How to set up linting, formatting, testing, and project structure before Claude Code writes your first feature.
When Claude Code writes your project, your role changes. You're not a typist. You're an architect and a reviewer. The code comes fast — sometimes faster than you can read it. Quality tooling is your review infrastructure. It catches what you miss.
This is Part 1 of a three-part series on setting up Python project quality when Claude Code is your developer. We cover the foundation here: the files and configuration that should exist before any application code does.
The pattern for each recommendation: what the tool does, a prompt you can paste into Claude Code, and what to verify after.
pyproject.toml — the project's constitution
Every Python tool reads pyproject.toml. Ruff, pytest, mypy, build backends — they all look here for configuration. One file, one source of truth.
More importantly for AI-assisted development: Claude Code reads it too. When your project has a well-configured pyproject.toml, Claude understands your conventions. Line length, linting rules, test paths — it picks these up automatically and generates code that conforms.
The prompt:
Create a pyproject.toml for a Python 3.11 project. Configure ruff
(line-length 120, select E/F/W/I/UP/B/SIM rules) and pytest
(testpaths = tests, quiet output with short tracebacks).What to verify: The file exists at the project root. ruff check . runs without config errors. pytest --co finds the test directory.
The generated file should look roughly like this:
[project]
name = "my-project"
version = "0.1.0"
requires-python = ">=3.11"
[tool.ruff]
line-length = 120
[tool.ruff.lint]
select = ["E", "F", "W", "I", "UP", "B", "SIM"]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q --tb=short"Directory structure with clean imports
Project layout determines whether imports work or break. Get it right once and forget about it.
The split: src/ for application code, tests/ for tests, scripts/ for operational scripts that aren't part of the package. A conftest.py in the test directory handles import path setup so tests can find your source code.
The prompt:
Set up the project directory structure: src/ for application code
with __init__.py, tests/ with a conftest.py that adds src/ to
sys.path, scripts/ for operational scripts, and docs/ for
documentation.What to verify: python -c "import src" works from the project root. Tests can import application modules without sys.path hacks scattered through every test file.
.gitignore — comprehensive from the start
Claude Code generates files. Ruff creates a cache. Pytest writes coverage data. Virtual environments contain thousands of files. None of these belong in version control.
Set up .gitignore before anything else, because cleaning up a repository that already tracked generated files is tedious.
The prompt:
Create a .gitignore for a Python project. Include __pycache__,
.venv, .env, dist, build, .ruff_cache, .mypy_cache, htmlcov,
.coverage, .DS_Store, *.egg-info, and .pytest_cache.What to verify: Run git status after creating and activating a virtual environment. No .venv/ files appear.
.editorconfig — consistency across tools
Different editors, different defaults. Claude Code generates code with one indentation style, your editor reformats it with another, the diff is noise.
.editorconfig standardizes whitespace rules across every tool that touches your files. Most editors respect it natively or via a plugin.
The prompt:
Create an .editorconfig: UTF-8, LF line endings, 4-space indent
for Python, tab indent for Makefile, 2-space for YAML. Trim
trailing whitespace.What to verify: File exists at the project root. Open a Python file in your editor — indentation should be 4 spaces.
Ruff — linter and formatter before line one
Ruff replaces flake8, isort, pyupgrade, and Black in a single tool. It's fast enough to run on every save without noticing.
Why this matters more with AI-generated code: Claude sometimes produces unused imports, inconsistent formatting, or shadowed variables. These aren't bugs — they're noise that accumulates. Ruff catches them automatically, keeping the codebase clean regardless of who (or what) wrote the code.
The prompt:
Install ruff and run it on the project. Fix any issues automatically.
Show me the results.What to verify: ruff check . returns zero findings. ruff format --check . reports no changes needed.
Running ruff after every Claude Code generation pass becomes a habit. Think of it as a spell checker for code style.
Makefile — your command palette
A Makefile gives every common operation a short, memorable name. Instead of remembering python -m pytest tests/ -q --tb=short, you type make test.
This matters for AI-assisted development because you can tell Claude Code "run make check" and it executes the exact same lint-then-test sequence you run locally. Consistent commands, consistent results.
The prompt:
Create a Makefile with these targets: install (pip install -r
requirements.txt), test (pytest), lint (ruff check), fmt (ruff
format + ruff check --fix), check (lint then test). All targets
should be .PHONY.What to verify: make check runs lint followed by tests. Both pass.
The generated Makefile:
.PHONY: install test lint fmt check
install:
pip install -r requirements.txt
test:
pytest
lint:
ruff check .
fmt:
ruff format .
ruff check --fix .
check: lint testrequirements.txt — track dependencies from the start
Two files: requirements.txt for production dependencies, requirements-dev.txt for development tools. The dev file includes the production file so you don't maintain the same list twice.
Minimum version pins (e.g., requests>=2.31) prevent silent breakage when a dependency releases a new major version. Pin early — retrofitting pins into a project with 30 unlocked dependencies is an afternoon you won't enjoy.
The prompt:
Create requirements.txt for production dependencies and
requirements-dev.txt that includes -r requirements.txt plus
pytest, pytest-cov, and ruff.What to verify: pip install -r requirements-dev.txt succeeds in a fresh virtual environment. pytest --version and ruff --version both work.
The day-one checklist
Seven files, seven prompts, zero application code:
pyproject.toml— tool configuration, project metadatasrc/__init__.py+tests/conftest.py— clean directory structure.gitignore— keep generated files out of version control.editorconfig— consistent whitespace across tools- Ruff configured and passing — linter and formatter active
Makefile— standard command interfacerequirements.txt+requirements-dev.txt— dependencies tracked
Every file here serves the same purpose: giving you confidence in code you didn't write by hand. When Claude Code generates a module tomorrow, ruff will lint it, pytest will test it, and make check will verify the whole project still works.
In Part 2, we add the safety net: tests, CI, pre-commit hooks, and secret scanning.
Where to run this
Every project needs a machine to run CI and host staging environments. Hetzner gets you a CX22 at €4.85/month with €10 in starting credit — more than enough for GitHub Actions self-hosted runners, a staging server, or both.
If Claude Code is building your project and you want the AI agent layer managed for you, xCloud runs OpenClaw hosted — so you can focus on prompts and code instead of infrastructure.
(Affiliate links — we get a small cut if you sign up, at no cost to you.)
If you're using an AI assistant to set up a project like this, paste the URL of this post into the conversation. It'll pick up the tool choices and prompt patterns and apply them to your specific setup.