This document provides guidance for AI assistants working on this codebase.
Run after every change:
uv run ruff check src tests && uv run ruff format src tests && uv run mypy src && uv run pytestThis is a Python project following Hexagonal Architecture (Ports and Adapters) with Test-Driven Development practices.
For detailed guidance on patterns and practices used in this project, see the skills in .claude/skills/:
| Skill | Description |
|---|---|
python-hexagonal-architecture.md |
Architecture patterns, folder structure, ports and adapters |
python-tdd-pytest.md |
TDD workflow, test organization, BDD-style naming, fakes |
python-uv-package-management.md |
uv commands, dependency management, lock files |
python-ruff-mypy-quality.md |
Linting, formatting, type checking configuration |
python-dataclasses-modeling.md |
Domain entities, value objects, immutability patterns |
python-protocols-interfaces.md |
Protocols vs ABCs, composition over inheritance |
python-src-layout.md |
src layout convention, imports, package structure |
python-dependency-injection.md |
Constructor injection, composition root, testing with fakes |
| Tool | Purpose | Command prefix |
|---|---|---|
| uv | Package manager and runner | uv run |
| ruff | Linting and formatting | uv run ruff |
| mypy | Static type checking | uv run mypy |
| pytest | Testing framework | uv run pytest |
Always use uv run to execute commands — this ensures the correct virtual environment and dependencies are used.
- Use
uv runfor all commands - Add type hints to all functions
- Write tests before implementation (TDD)
- Use
@pytest.mark.uniton test classes for use case tests - Use
@pytest.mark.integrationon test classes for adapter tests - Use fake implementations of ports for testing use cases
- Use
given_when_thennaming pattern for test methods - Prefer Protocols over ABCs for interfaces
- Prefer composition over inheritance
- Never import from
adapters/insidedomain/ - Never use
pipdirectly — useuv - Never skip running checks after changes
- Never put framework dependencies in
domain/
| Type | Location |
|---|---|
| Entities / Value Objects | src/example_app/domain/models/ |
| Port interfaces | src/example_app/domain/ports/ |
| Use cases | src/example_app/domain/use_cases/ |
| Inbound adapters | src/example_app/adapters/inbound/ |
| Outbound adapters | src/example_app/adapters/outbound/ |
| Tests | Mirror the source path under tests/ |
- Create test file:
tests/domain/use_cases/test_<name>.py - Write test class with
@pytest.mark.unitdecorator - Write failing test with
given_when_thennaming - Create use case:
src/example_app/domain/use_cases/<name>.py - Implement until tests pass
- Run checks:
uv run ruff check src tests && uv run ruff format src tests && uv run mypy src && uv run pytest
- Create test file:
tests/adapters/outbound/test_<name>.py(orinbound/) - Write test class with
@pytest.mark.integrationdecorator - Write failing test
- Create adapter:
src/example_app/adapters/outbound/<name>.py - Implement the port interface
- Run checks
- Create interface:
src/example_app/domain/ports/<name>.py - Define Protocol (preferred) or ABC with
@abstractmethoddecorators - Run checks
uv run pytest # All tests
uv run pytest -m unit # Only unit tests
uv run pytest -m integration # Only integration tests
uv run pytest tests/domain # By path
uv run pytest --cov=example_app --cov-report=html # With coverage- Follow TDD - Write failing tests first, then implement
- Keep the domain pure - No framework dependencies in
domain/ - Use ports for external communication - Never call external systems directly from use cases
- Mirror test structure - New source files should have corresponding test files
- Apply appropriate markers - Use
@pytest.mark.unitor@pytest.mark.integration - Always run checks after making changes:
uv run ruff check src tests && uv run ruff format src tests && uv run mypy src && uv run pytest