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: ```python 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): ```python def foo( x:int,y:int )->int: return x+y ``` Nach `ruff format` könnte das so aussehen: ```python 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: ```python import myproject.utils import os import sys import requests from math import sqrt from math import ceil ``` Nach ruff (vereinfacht): ```python 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`: ```bash pip install ruff ``` Oder als dev-Abhängigkeit in `pyproject.toml` (Poetry/uv/pip-tools etc.). --- ### 6.2. Einfacher Aufruf Linting: ```bash ruff check . ``` Mit automatischen Fixes: ```bash ruff check . --fix ``` Formatierung: ```bash ruff format . ``` --- ### 6.3. Minimale Konfiguration in `pyproject.toml` ```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: ```python 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: ```python def process(items): count = len(items) if items is None: print("No items") return count ``` --- ### 6.5. Beispiel: Imports aufräumen Code: ```python 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`: ```python 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](https://pre-commit.com/), damit Checks automatisch vor jedem Git-Commit laufen. `.pre-commit-config.yaml`: ```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).