Skip to content

Software Development Directory Structure

A well-organized directory structure is just as important as clean, consistent code style — it significantly improves a project’s readability and maintainability. This article covers the recommended directory layout for Python projects and the modern toolchain that supports them.

Traditional Directory Structure

Suitable for small-to-medium scripts and application projects:

myapp/
├── core/             # Core business logic
│   └── engine.py
├── api/              # External interface layer
│   └── routes.py
├── db/               # Database operations
│   └── models.py
├── lib/              # Internal shared library
│   └── utils.py
├── conf/             # Configuration files
│   └── settings.py
├── tests/            # Test code
│   ├── __init__.py
│   └── test_engine.py
├── run.py            # Application entry point
├── requirements.txt  # Dependency list
└── README.md         # Project description
  • core/: Business logic; no I/O or network code here, making unit testing straightforward.
  • api/: External interfaces that call into the logic in core/.
  • db/: ORM models and queries, decoupled from business logic.
  • lib/: Utility functions, decorators, custom exceptions, and other cross-module reusable code.
  • conf/: Central location for all configuration; separate dev/prod environments here.
  • run.py: The startup script at the project root; its directory is automatically added to sys.path at runtime.

Modern Python Package Structure (src Layout)

Recommended for libraries intended for public release or projects installed via pip install:

mypackage/
├── src/
│   └── mypackage/       # Package code lives under src/ to prevent accidental local imports
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── conftest.py
│   └── test_core.py
├── pyproject.toml       # Modern project configuration (replaces setup.py)
├── README.md
└── .gitignore

pyproject.toml — Modern Project Configuration

Python 3.11+ recommends pyproject.toml as the single source of truth for project metadata, dependencies, and tool configuration:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "mypackage"
version = "0.1.0"
description = "An example Python package"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "requests>=2.31",
    "pydantic>=2.0",
]

[project.optional-dependencies]
dev = ["pytest>=8.0", "ruff>=0.4", "mypy>=1.9"]

[tool.ruff]
line-length = 88

[tool.ruff.lint]
select = ["E", "F", "I"]  # pycodestyle, pyflakes, isort

[tool.mypy]
python_version = "3.12"
strict = true

requirements.txt

Used to record dependencies so the environment can be reproduced quickly:

requests==2.31.0
pydantic==2.7.0
fastapi==0.111.0
uvicorn==0.29.0

How to generate it:

# Traditional approach
pip freeze > requirements.txt

# Recommended: use uv (a faster pip alternative)
uv pip freeze > requirements.txt

Modern Toolchain

ToolPurpose
uvBlazing-fast package manager and virtual environment tool (written in Rust)
ruffBlazing-fast linter and formatter (replaces flake8 + isort + black)
mypy / pyrightStatic type checking
pytestTesting framework
hatch / flitPackage build tools

Getting Started with uv

# Install uv
pip install uv

# Create a virtual environment and install dependencies
uv venv
uv pip install -r requirements.txt

# Run the project
uv run python run.py

# Add a dependency
uv add requests

Key Conventions

  • One virtual environment per project — avoid polluting the global Python installation with project-specific packages.
  • Separate configuration from code — sensitive data (API keys, database passwords) should be injected via environment variables or a .env file and must never be committed to the repository.
  • __init__.py — marks a directory as a Python package; can be empty or can export the package’s public API.
  • if __name__ == "__main__": — place script logic under this guard so a module can be both imported and run directly.
  • README.md — every project must include: a description of what it does, installation steps, a quick-start example, and an explanation of the directory structure.
Last updated on