Januar 12, 2026

Automatisierte Code-Qualität in Data-Science-Projekten

Vor allem in Data-Science-Projekten wächst Code oft organisch: schnelle Analysen, viele Notebooks, wenig Struktur. Doch spätestens, wenn ein Modell produktiv gehen oder ein Team gemeinsam entwickeln soll, werden Codequalität und Reproduzierbarkeit entscheidend.

Automatisierte Code-Qualität ist dabei kein Luxus, sondern ein entscheidender Hebel für Nachvollziehbarkeit, Wartbarkeit und Vertrauen in Ergebnisse – besonders, wenn Machine-Learning-Pipelines oder APIs im Spiel sind.

Dieser Beitrag zeigt, wie du mit einer zentralen Konfiguration in pyproject.toml, Pre-Commit Hooks und GitHub Actions eine nachhaltige Code-Qualitätsstrategie aufbaust.

Ziel: Qualität automatisieren statt manuell kontrollieren

Manuelle Code-Reviews und Formatierungen sind zeitaufwendig und fehleranfällig. Stattdessen helfen verschiedene Stufen der Automatisierung:

  1. Zentrale Konfiguration – alles in pyproject.toml gebündelt
  2. Pre-Commit Hooks – lokale Formatierung und Checks vor jedem Git-Commit
  3. Makefile – einheitliche lokale Workflows für alle Entwickler:innen
  4. CI/CD Pipeline – automatisierte Qualitätssicherung bei jedem Push

Das funktioniert für klassische Softwareprojekte genauso wie für Data Science Pipelines.

Linting, Formatting und Typprüfung im Überblick

ToolAufgabeNutzen
BlackFormatierung nach klaren RegelnEinheitlicher Stil, keine Diskussionen
RuffLinter + isort-ErsatzFindet Stil-, Syntax- und Performance-Probleme
MypyTypprüfungErkennt Fehler frühzeitig dank Type Hints

Zentrale Konfiguration mit pyproject.toml

Python bietet seit PEP 518 die Möglichkeit, Tool-Konfigurationen in einer zentralen Datei zu pflegen. Die Datei pyproject.toml wird im Hauptverzeichnis des Repositories abgelegt. Diese Datei dient als Meta-Konfiguration für alle relevanten Tools – Formatierung, Linting, Typprüfung, Build-System etc. In dieser Datei wird unter anderem Ruff und Black konfiguriert.

In der Section [project] werden grundsätzliche Infos wie Name oder Versionsnummer festgehalten. Dort kann man sogar die dependencies festhalten (das wird bspw. von Poetry verwendet). Wenn klassische requirements.txt mit pip verwendet wird, dann ist das nicht notwendig.

Ruff und Black dienen zur Code Formatierung und Linting.

Mypy ist der Type Checker für Python. Er prüft, ob der Code mit den Typannotationen übereinstimmt – ähnlich wie TypeScript bei JavaScript.

Hinweis: In VSCode wird die toml nicht automatisch erkannt, dafür benötigt man die Extension Suche nach TOML Language Support.

[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project]
name = "fizzbuzz-api"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
  "fastapi>=0.111",
  "uvicorn[standard]>=0.29",
  "pydantic>=2.7",
  "httpx>=0.27,<0.29",
]

[tool.ruff]
line-length = 100
target-version = "py311"
# Wähle ein gutes Basis-Set an Regeln:
lint.select = ["E", "F", "I", "UP", "B", "C4", "SIM", "PL"]
lint.ignore = [
  "E501",  # Länge übernimmt black
]
# isort via Ruff:
src = ["app", "tests"]

[tool.black]
line-length = 100
target-version = ["py311"]

[tool.mypy]
python_version = "3.11"
warn_unused_configs = true
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
no_implicit_optional = true
check_untyped_defs = true
strict_equality = true
pretty = true
show_error_codes = true
# FastAPI/Pydantic:
plugins = ["pydantic.mypy"]
# Wo wir streng prüfen:
mypy_path = "."

# Paket-spezifische Ausnahmen (falls nötig)
[[tool.mypy.overrides]]
module = ["uvicorn.*", "starlette.*"]
ignore_missing_imports = true

Entwicklungsabhängigkeiten: requirements-dev.txt

Damit CI, lokale Tests und Pre-Commit denselben Stand nutzen, empfiehlt sich eine dedizierte Datei für das lokale Testing und Formatier anzulegen.

-r requirements.txt
black>=24.0
ruff>=0.5.0
mypy>=1.10
pydantic>=2.7
pydantic-settings>=2.4 ; python_version >= "3.11"  # optional, falls du Settings nutzt
pytest>=8.4
pytest-cov>=5.0

Pre-commit Hooks

Mit pre-commit kann man bereits vor jedem Commit lokal die Code-Formatierungen optimieren. Dazu installiert man das Paket pre-commit. Die Einstellungen werden in der Datei pre-commit-config.yaml festgelegt:

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.5.7
    hooks:
      - id: ruff
        args: ["--fix"]   # auto-fix wo möglich
      - id: ruff-format   # ruff-format (alternativ zu black, wenn du magst)

  # Falls du black lieber separat willst, aktiviere diesen Block und entferne ruff-format oben:
  - repo: https://github.com/psf/black
    rev: 24.8.0
    hooks:
      - id: black

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.10.0
    hooks:
      - id: mypy
        additional_dependencies: ["pydantic>=2.7", "pydantic-core>=2.18"]

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: check-merge-conflict
      - id: end-of-file-fixer
      - id: trailing-whitespace

Um die pre-commit auszuführen benötigt man folgenden Befehl:

pre-commit run --all-files

Tipp: In Teams lohnt es sich, pre-commit als Pflichtschritt in der Onboarding-Dokumentation oder Makefile zu hinterlegen.

Makefile: Automatisierung für Entwickler:innen

Ein Makefile ist eine einfache, aber mächtige Möglichkeit, häufige Befehle in standardisierte Kurzbefehle (Targets) zu verpacken.
Es stammt ursprünglich aus der C-Entwicklung, wird heute aber in Python-, Data-Science- und DevOps-Projekten häufig genutzt.

Komplexe Befehle werden einmal ins Makefile überführt und dann mit einem einfachen make-Befehl ausgeführt.

.PHONY: install lint format test typecheck quality

install:
	pip install -r requirements-dev.txt

lint:
	ruff check .

format:
	black .
	ruff format .

typecheck:
	mypy .

test:
	pytest -q --disable-warnings --maxfail=1

quality: lint format typecheck test

Erklärung:

  • .PHONY bedeutet, dass die Befehle keine echten Dateien sind, sondern Tasks.
  • Jeder Abschnitt (install, lint, test …) ist ein Target, das mit make <target> ausgeführt wird.
  • Man kann Targets kombinieren oder Abhängigkeiten definieren, z. B. quality: lint test bedeutet, dass make quality beide Schritte ausführt.

Beispiele zur Anwendung:

# Erstinstallation
make install
# Codequalität prüfen
make quality
# Nur Formatierung
make format 

In Data-Science-Projekten kann man Makefiles auch nutzen um Daten-Tasks zu automatisieren:

data-clean:
	python scripts/clean_data.py --input data/raw --output data/clean

train-model:
	python src/train_model.py --config configs/model.yaml

CI Pipeline mit Github Actions

Automatisierte Qualitätssicherung in der Cloud ist der letzte Baustein: Jeder Commit oder Pull Request löst eine Continuous Integration (CI)-Pipeline aus. Um die CI Pipeline aufzusetzen erstellt man die Datei .github/workflows/ci.yaml

name: CI

on:
  pull_request:
  push:
    branches: [ main ]

jobs:
  quality-test-docker:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.11"
          cache: "pip"

      - name: Install dev deps
        run: |
          python -m pip install --upgrade pip
          python -m pip install -r requirements-dev.txt

      - name: Lint (ruff)
        run: ruff check .

      - name: Format check (black)
        run: black --check .

      - name: Type check (mypy)
        run: mypy .

      - name: Run tests
        run: pytest -q --maxfail=1 --disable-warnings

      - name: Build Docker image
        uses: docker/setup-buildx-action@v3

      - name: Docker build (no push)
        run: docker build -t fizzbuzz-api:ci .

      # Optional: Container kurz starten und Healthcheck testen
      - name: Smoke test container
        run: |
          docker run -d -p 8000:8000 --name fb fizzbuzz-api:ci
          sleep 2
          curl -f http://127.0.0.1:8000/health
          docker rm -f fb

So wird bei jedem Commit sichergestellt, dass:

  • Der Code korrekt formatiert ist,
  • Typfehler früh erkannt werden,
  • Tests fehlerfrei laufen.

Data-Science-Tipp:
Diese CI-Struktur lässt sich leicht erweitern – z. B. für MLflow-Modelle, Docker Builds, oder automatisierte Data Validation mit Great Expectations.

Fazit

Automatisierte Codequalität macht Projekte nachhaltig und teamfähig. Gerade in Data-Science-Teams, wo Analysecode oft später zur API oder zum Produktionsskript wird, lohnt sich dieser Aufwand frühzeitig.

Weiterführende Quellen

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert