2. 开发环境与工程化项目初始化

企业级 Python 后端项目与个人脚本最大的区别,不在于代码量,而在于“可复现”。同一份代码,在同事的机器、CI 流水线和生产容器里必须得到一致的依赖版本、一致的代码风格、一致的检查结果。本节从零搭建一个符合企业规范的后端项目骨架,把虚拟环境、依赖管理、格式化、静态检查、pre-commit 钩子和目录结构串成一条可执行的工程化链路。

一、虚拟环境:隔离是第一原则

不要用系统 Python 直接装依赖。推荐使用 uv 或 poetry 管理虚拟环境与依赖,二者都能锁定版本并生成锁文件。以 uv 为例,初始化项目并指定 Python 版本:

uv init backend-skeleton --python 3.12
cd backend-skeleton
uv venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate

虚拟环境目录 .venv/ 必须写入 .gitignore,绝不提交。团队协作时,每个人通过锁文件重建环境,而不是互相拷贝 site-packages。

二、依赖管理:区分运行时与开发依赖

企业项目要把“运行时依赖”和“开发工具依赖”分开。运行时依赖是服务启动必需的,如 fastapi、sqlalchemy、pydantic;开发依赖只在本地和 CI 使用,如 ruff、mypy、pytest、pre-commit。使用 uv 添加依赖:

uv add fastapi "uvicorn[standard]" sqlalchemy pydantic-settings
uv add --dev ruff mypy pytest pre-commit

执行后 pyproject.toml 会记录依赖声明,uv.lock 记录精确版本与哈希。锁文件必须提交到仓库,它是“可复现”的核心凭证。CI 中使用 uv sync --frozen 安装,若锁文件与声明不一致会直接失败,避免“本地能跑、线上报错”。

三、代码格式化与静态检查

格式化解决“长什么样”,静态检查解决“有没有错”。推荐 ruff 同时承担格式化与 lint,mypy 负责类型检查。在 pyproject.toml 中集中配置:

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP", "SIM"]

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

其中 I 负责 import 排序,B 捕获常见 bug,UP 提示可用新语法,SIM 简化冗余写法。mypy 开启 strict 后,未标注类型的函数、隐式 Any、未使用的 type: ignore 都会报错。执行方式:

uv run ruff format .
uv run ruff check . --fix
uv run mypy src

判断标准很明确:ruff format 后无 diff,ruff check 退出码为 0,mypy 输出 Success: no issues found。任何一项不通过,都不应进入提交环节。

四、pre-commit:把检查前移到提交前

靠人自觉执行检查不可靠,pre-commit 钩子能在 git commit 时自动拦截不合格代码。创建 .pre-commit-config.yaml:

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.6.9
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.11.2
    hooks:
      - id: mypy
        additional_dependencies: [pydantic]

安装并运行:

uv run pre-commit install
uv run pre-commit run --all-files

钩子执行顺序是:先 ruff --fix 自动修复可修复问题,再 ruff-format 统一格式,最后 mypy 做类型检查。若 ruff 修改了文件,本次提交会被中止,需要重新 git add 后再提交。这是正常行为,不是报错。

五、项目目录结构规范

推荐采用 src 布局,把业务代码放在 src/ 下,避免测试时误导入根目录同名模块。一个可直接使用的骨架如下:

backend-skeleton/
├── pyproject.toml
├── uv.lock
├── .pre-commit-config.yaml
├── .gitignore
├── README.md
├── src/
│   └── app/
│       ├── __init__.py
│       ├── main.py            # FastAPI 入口
│       ├── core/
│       │   ├── config.py      # pydantic-settings 配置
│       │   └── logging.py
│       ├── api/
│       │   └── v1/
│       │       ├── router.py
│       │       └── endpoints/
│       │           └── health.py
│       ├── models/            # ORM 模型
│       ├── schemas/           # Pydantic 模型
│       └── services/          # 业务逻辑
└── tests/
    ├── conftest.py
    └── test_health.py

分层职责要清晰:api 只做参数校验和响应组装,services 承载业务逻辑,models 与 schemas 分别对应数据库和接口契约。这样后续接入数据库、缓存、消息队列时,改动范围可控。

六、最小可运行验证

在 src/app/main.py 写入一个健康检查接口:

from fastapi import FastAPI

app = FastAPI(title="backend-skeleton")

@app.get("/health")
def health() -> dict[str, str]:
    return {"status": "ok"}

启动并验证:

uv run uvicorn app.main:app --reload --port 8000
curl http://127.0.0.1:8000/health
# {"status":"ok"}

当 curl 返回 {"status":"ok"},且 pre-commit run --all-files 全部通过,说明骨架已具备企业项目的基本工程能力:环境可复现、依赖可锁定、风格可统一、错误可拦截、结构可扩展。

企业级 Python 后端项目工程化链路 从环境隔离到提交拦截,每一环都可复现、可验证 ① 环境与依赖 uv init / uv venv uv add(运行时依赖) uv add --dev(开发依赖) uv.lock 锁定精确版本 ② 格式化与静态检查 ruff format(统一风格) ruff check(lint 规则) mypy --strict(类型检查) pyproject.toml 集中配置 ③ pre-commit 钩子 git commit 触发 ruff --fix → ruff-format mypy 类型检查 失败则中止提交 ④ src 布局目录结构 src/app/ tests/ pyproject.toml api/v1/ services/ models/ + schemas/ core/config.py main.py .pre-commit-config.yaml 分层职责:api 校验 → services 业务 → models/schemas 数据契约 ⑤ 验证与判断 uv run uvicorn app.main:app curl /health → {"status":"ok"} pre-commit run --all-files 通过 三项全绿 = 骨架具备企业工程能力 环境 → 质量 → 拦截 → 结构 → 验证,形成可复现闭环 失败则修复后重提
建议把 uv run ruff format . && uv run ruff check . && uv run mypy src 写进 Makefile 或 justfile,本地与 CI 调用同一条命令,避免“本地通过、CI 失败”的差异。
不要把 .venv/、__pycache__/、.mypy_cache/、.ruff_cache/ 提交到仓库;同时确保 uv.lock 与 .pre-commit-config.yaml 必须提交,否则团队无法复现环境与检查规则。