Files
it-know-how/python/ruff.md
T
2026-03-27 12:58:01 +01:00

13 KiB
Executable File
Raw Blame History

Ruff ist ein relativ neues, sehr schnelles Tool zur Verbesserung der Code-Qualität in Python. Es kombiniert Funktionen, für die man früher mehrere Werkzeuge brauchte (Linter, Formatter, Import-Sortierer, teilweise Sicherheits-Checks), in einem Programm.

Ich gehe Schritt für Schritt durch:

  1. Grundidee und Zielgruppe
  2. Was macht ruff konkret?
  3. Abgrenzung zu ähnlichen Tools
  4. Welche Probleme löst ruff?
  5. Typische Herausforderungen im Einsatz
  6. Praxisnahe Beispiele (inkl. Konfiguration)

1. Grundidee von ruff in einfachen Worten

Stell dir vor, mehrere Entwickler:innen arbeiten an einem Python-Projekt. Ohne Regeln sieht der Code schnell sehr unterschiedlich aus:

  • Mal sind Leerzeichen anders gesetzt,
  • mal werden nicht genutzte Variablen liegen gelassen,
  • vielleicht wird an manchen Stellen unsicherer Code geschrieben,
  • im schlimmsten Fall fallen bestimmte Fehler erst spät auf.

Bisher nutzte man typischerweise mehrere Tools:

  • ein Linter (z.B. flake8 oder pylint),
  • einen Formatter (z.B. black),
  • einen Import-Sortierer (z.B. isort),
  • evtl. ein Security-Tool (z.B. bandit),
  • plus diverse Plugins.

Ruff will das bündeln:

  • Ein Tool für viele Code-Qualitätsaspekte
  • Sehr schnell (geschrieben in Rust, nicht in Python)
  • Einfache Konfiguration (alles typischerweise in pyproject.toml)
  • Viele Checks, die sich an bekannten Tools orientieren (flake8-Plugins, isort-Regeln, bandit-Regeln usw.)

2. Was macht ruff konkret?

2.1. Linting (Fehler und Stilprobleme finden)

Ruff untersucht deinen Python-Code und meldet z.B.:

  • Syntax-Fehler
  • Ungenutzte Variablen oder Importe
  • Logische Stolperfallen (z.B. == None statt is None)
  • Stil-Regeln (z.B. maximale Zeilenlänge)
  • Potenziell unsichere Konstruktionen

Beispiel:

def calculate(a, b):
    result = a + b
    unused = 42
    if a == None:
        print("a is None")
    return result

Ruff würde in etwa melden (vereinfacht):

  • unused wird nie verwendet
  • a == None besser a is None

Viele dieser Probleme kann ruff auch automatisch beheben (--fix).


2.2. Formatierung (Code automatisch „schön“ machen)

Ruff hat inzwischen einen eigenen Formatter, der ähnlich wie black funktioniert:

  • Einheitliche Einrückungen und Zeilenumbrüche
  • Klammer-Formatierung
  • Konsistente Verwendung von Anführungszeichen (je nach Einstellung)
  • Entfernung überflüssiger Leerzeilen usw.

Beispiel (unformatierter Code):

def foo(  x:int,y:int   )->int:
  return  x+y

Nach ruff format könnte das so aussehen:

def foo(x: int, y: int) -> int:
    return x + y

2.3. Imports sortieren und aufräumen

Ruff kann ähnlich wie isort:

  • Importe sortieren (alphabetisch und nach Gruppen: Standardbibliothek, Drittanbieter, Projektcode)
  • Unbenutzte Importe entfernen

Beispiel:

import myproject.utils
import os
import sys
import requests

from math import sqrt
from math import ceil

Nach ruff (vereinfacht):

import os
import sys
from math import ceil, sqrt

import requests

import myproject.utils

Und wenn sys gar nicht verwendet wird, kann ruff es auch entfernen.


2.4. Ein Tool mehrere Regel-Sammlungen

Ruff bringt rule sets mit, die vielen bekannten Tools entsprechen, z.B.:

  • E/F/W: Pycodestyle/Pyflakes-ähnlich (via flake8)
  • I: isort-Regeln
  • N: pep8-naming (Namenskonventionen)
  • S: bandit (Sicherheitsregeln)
  • UP: pyupgrade (veraltete Syntax, modernisieren)
  • u.v.m.

Du kannst über die Konfiguration steuern, welche Regel-Gruppen du aktivierst oder deaktivierst.


3. Abgrenzung zu ähnlichen Tools

3.1. Ruff vs. flake8 (+ Plugins)

flake8 ist ein Linter; für viele Extras braucht man Plugins:

  • z.B. flake8-bugbear, flake8-import-order, pep8-naming usw.

Ruff:

  • Bietet die Funktionalität vieler flake8-Plugins „eingebaut“.
  • Ist deutlich schneller (insbesondere bei großen Projekten).
  • Wird oft als Drop-in-Ersatz für flake8 verwendet.

Aber:

  • flake8 ist schon lange etabliert; manche Teams haben stark angepasste flake8-Setups, die man nicht 1:1 nach ruff übertragen kann.
  • Einige Spezial-Plugins existieren ggf. nur für flake8.

3.2. Ruff vs. pylint

pylint:

  • Sehr umfangreicher Linter (viele komplexe Regeln, u.a. über Projektstruktur, OOP-Patterns usw.)
  • Langsam im Vergleich zu ruff
  • Detaillierte Reports und Scores

Ruff:

  • Fokus auf Geschwindigkeit und auf Regeln, die sich gut automatisieren/auto-fixen lassen.
  • Viele „klassische“ Lint-Regeln, Naming, Imports, Security-Basics, aber nicht alle tiefgehenden Analysen von pylint.
  • Für komplexe Architektur-Regeln wird weiterhin oft pylint oder andere Tools genutzt.

3.3. Ruff vs. black

black:

  • Reiner Formatter macht nur Formatierung, keine Lint-Fehler (bis auf ganz wenige Ausnahmen).
  • Sehr stabile, strikte Formatierung (Meinung: „The uncompromising code formatter“).

Ruff:

  • Eigenständiger Formatter, der in vielen Projekten Black ersetzen kann.
  • Zusätzlich: Linting, Import-Sortierung, Security-Regeln usw.
  • Du kannst:
    • nur ruff als Formatter nutzen, oder
    • ruff als Linter + black als Formatter (dann ruff format nicht verwenden), wenn dein Team bereits stark auf black setzt.

3.4. Ruff vs. isort

isort:

  • Spezialisiert auf das Sortieren von Imports.

Ruff:

  • Hat eine integrierte Import-Sortierung (Regelgruppe I).
  • Für die meisten Fälle reicht ruff völlig aus.
  • Wenn ihr sehr spezielle Import-Sortierregeln braucht, ist isort manchmal noch flexibler, aber das wird immer weniger relevant.

3.5. Ruff vs. mypy/pyright (Typprüfung)

mypy/pyright:

  • Statische Typprüfer: sie prüfen, ob die Typannotationen sinnvoll zusammenpassen.
  • Finden z.B. Fehler wie: „Funktion gibt laut Typ str zurück, tatsächlich aber int“.

Ruff:

  • Enthält Regeln, die mit Typannotationen arbeiten (z.B. Style, Safety), aber kein vollwertiger Typprüfer.
  • Typfehler (im Sinne von mypy) sollten weiterhin mit mypy oder pyright geprüft werden.

3.6. Ruff vs. bandit (Security)

bandit:

  • Spezialisiertes Security-Tool für Python.

Ruff:

  • Hat viele bandit-Regeln integriert (Regelgruppe S).
  • Deckt gängige Sicherheitsfallen ab (z.B. eval auf untrusted Input, hartkodierte Passwörter etc.).
  • Für tiefgehende Security-Audits kann ein spezialisiertes Tool trotzdem sinnvoll sein.

4. Welche Probleme löst ruff?

4.1. Performance-Probleme in großen Projekten

Früher:

  • flake8 + black + isort + bandit + mypy
    → viele Tools, mehrfaches Einlesen des Codes, CI dauert lange.

Mit ruff:

  • Ein Tool übernimmt Linting, Formatierung, Importe, einen großen Teil der Security-Regeln.
  • Deutlich weniger Laufzeit, besonders in CI-Pipelines oder bei großen Repositories.

4.2. Zu viele Tools, komplizierte Konfiguration

Problem:

  • Unterschiedliche Konfigurationsdateien (.flake8, pyproject.toml, setup.cfg, .isort.cfg, pyproject.toml für black…)
  • Mehr Aufwand beim Onboarding neuer Teammitglieder.

Ruff:

  • Typischerweise alles in einer Konfiguration (pyproject.toml).
  • Weniger bewegliche Teile, einfachere Wartung.

4.3. Inkonsequente Codequalität im Team

Ohne einheitliche Tools:

  • Jede:r schreibt etwas anders.
  • Diskussionen in Code-Reviews drehen sich um Stil statt Inhalte.
  • Fehler (z.B. ungenutzte Variablen, potentielle Bugs) werden erst spät bemerkt.

Mit ruff:

  • Gemeinsame, automatisierte Regeln.
  • Automatische Fixes für vieles (z.B. im Editor oder Pre-Commit-Hooks).
  • Code-Reviews können sich auf Architektur und Logik konzentrieren.

4.4. Technische Schulden reduzieren

In älteren Projekten:

  • Viele kleine Stil- und Qualitätsprobleme haben sich angesammelt.
  • Niemand möchte „alles mal eben aufräumen“, weil Tools zu langsam sind oder es zu viel ist.

Mit ruff:

  • Durch die Geschwindigkeit kann man auch große Codebasen lintern.
  • Schrittweise Verbesserung möglich: z.B. zunächst nur wichtige Regelgruppen aktivieren, später mehr.

5. Herausforderungen bei der Nutzung von ruff

5.1. Zu viele Meldungen am Anfang

Wenn du ruff das erste Mal auf ein älteres Projekt loslässt, bekommst du oft hunderte oder tausende Meldungen.

Strategie:

  • Nur einen Teil der Regeln aktivieren (z.B. nur „kritische“ oder klar hilfreiche).
  • Bestehende Verstöße einmalig ignorieren (per --ignore oder --per-file-ignores) und neue Verstöße blocken.
  • Nach und nach alte Stellen aufräumen.

5.2. Regeln verstehen und anpassen

Ruff hat sehr viele Regeln. Nicht alle passen zu jedem Projekt.

  • Manche Regeln sind sehr streng (z.B. bestimmte Naming- oder Docstring-Regeln).
  • Du musst überlegen: Welche Regeln sind für unser Team sinnvoll?

Lösung:

  • Regeln gezielt aktivieren/deaktivieren.
  • Dokumentieren, warum bestimmte Regeln aktiv oder abgeschaltet sind.

5.3. Wechsel von bestehender Tool-Landschaft

Wenn ihr schon flake8, black, isort etc. nutzt:

  • Müssen Einstellungen in ruff nachgebaut werden.
  • Manche Teams werden black nicht sofort durch ruff format ersetzen wollen.

Ein pragmatischer Weg:

  1. Zuerst ruff als Linter einführen (Formatierung bleibt bei black).
  2. Wenn gewünscht, später ruff format testen und ggf. black ersetzen.

5.4. Auto-Fixes mit Vorsicht genießen

Ruff kann sehr viel automatisch reparieren:

  • In der Regel gut, aber:
    • Bei manchen Regeln sollte man prüfen, ob die Änderung wirklich die Absicht trifft.
    • In kritischen Bereichen (z.B. Security-sensible Logik) ggf. Auto-Fixes nicht blind akzeptieren.

Empfehlung:

  • Auto-Fixes lokal ausführen, dann diff anschauen.
  • In CI eher nur prüfen, nicht fixen.

5.5. Editor-Integration

Ruff hat gute Unterstützung in vielen Editoren (VS Code, PyCharm, Neovim usw.), aber:

  • Man muss oft ein Plugin oder eine Extension installieren.
  • Manchmal überschneidet sich das mit vorhandenen Tools (z.B. Black-Extension vs. Ruff-Formatter); das muss sauber konfiguriert werden.

6. Praxisnahe Beispiele

6.1. Installation von ruff

Typisch über pip:

pip install ruff

Oder als dev-Abhängigkeit in pyproject.toml (Poetry/uv/pip-tools etc.).


6.2. Einfacher Aufruf

Linting:

ruff check .

Mit automatischen Fixes:

ruff check . --fix

Formatierung:

ruff format .

6.3. Minimale Konfiguration in pyproject.toml

[tool.ruff]
target-version = "py311"      # welche Python-Version ihr nutzt
line-length = 88              # maximale Zeilenlänge
exclude = ["venv", ".venv", "build", "dist"]

[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP"]   # Regelgruppen: Errors, Flake, Imports, Bugbear, pyupgrade
ignore = ["E501"]                     # Beispiel: lange Zeilen ignoriere ich (wenn ich black nutze)

[tool.ruff.format]
# Beispiel: wenn ihr black-kompatible Einstellungen wollt
quote-style = "double"
indent-style = "space"
line-ending = "auto"
  • select gibt an, welche Regelgruppen aktiv sind.
  • ignore hebt einzelne Regelcodes auf (z.B. E501 für Zeilenlänge).

6.4. Beispiel: Unbenutzte Variable und schlechtes None-Checking

Code:

def process(items):
    count = len(items)
    unused = 0

    if items == None:
        print("No items")
    return count

Ruff meldet u.a.:

  • F841 (unused variable unused)
  • E711 (Vergleich mit None mit == statt is)

Mit ruff check . --fix könnte daraus werden:

def process(items):
    count = len(items)

    if items is None:
        print("No items")
    return count

6.5. Beispiel: Imports aufräumen

Code:

import os
import sys
import json
import requests

def get_data(url):
    r = requests.get(url)
    return r.json()

Hier wird os, sys und json nicht genutzt.

Ruff meldet:

  • F401 (unused imports: os, sys, json)

Nach ruff check . --fix:

import requests


def get_data(url):
    r = requests.get(url)
    return r.json()

6.6. Beispiel: Einsatz mit pre-commit

Viele Teams nutzen pre-commit, damit Checks automatisch vor jedem Git-Commit laufen.

.pre-commit-config.yaml:

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.6.5  # Beispielversion, ggf. aktualisieren
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

Damit werden:

  • Linting + Auto-Fix vor jedem Commit ausgeführt,
  • Formatierung automatisch angewendet.

7. Zusammenfassung

  • Ruff ist ein modernes, sehr schnelles Tool zur Verbesserung der Code-Qualität in Python.
  • Es kombiniert:
    • Linting (Fehler und Stil),
    • Formatierung,
    • Import-Sortierung,
    • viele Sicherheits- und Modernisierungsregeln.
  • Es ersetzt in vielen Projekten:
    • flake8 (+ viele Plugins),
    • isort,
    • teilweise black,
    • teilweise bandit.
  • Es löst Probleme wie:
    • lange Tool-Laufzeiten,
    • fragmentierte Konfiguration,
    • inkonsistente Codequalität.
  • Herausforderungen sind v.a.:
    • Auswahl und Anpassung der Regeln,
    • Umstieg von bestehenden Tools,
    • Umgang mit großen Mengen an Anfangs-Warnungen.

Wenn du möchtest, kann ich dir im nächsten Schritt eine konkrete Beispielkonfiguration für ein kleines oder ein großes Projekt vorschlagen (inkl. welche Regelgruppen sinnvoll sind und welche man am Anfang lieber auslässt).