# Ruff

## Hvad det er

Ruff er et enkelt kommandolinjeprogram, skrevet i Rust, der holder
Python-kildekode op mod et katalog af lintregler og omskriver dens layout til én
ensartet stil. Bag det står Astral, firmaet bag uv og ty, og to af
underkommandoerne klarer det daglige arbejde: `ruff check` er linteren, og
`ruff format` er formatteren. Ruff hævder at køre 10 til 100 gange hurtigere end
Flake8 og Black, gemmer en cache, så uændrede filer ikke bliver analyseret igen,
og kan træde i stedet for Flake8 med snesevis af plugins, Black, isort,
pydocstyle, pyupgrade og autoflake.

Linteren har over 900 regler, mange af dem genskrevet fra populære
Flake8-plugins. Hver regel har en kode af et kort præfiks og tre cifre, og
præfikset fortæller, hvor reglen kommer fra: F401, en ubrugt import, stammer fra
Pyflakes, og E501, en linje der er for lang, fra pycodestyle. En konfiguration
vælger regler ud fra koderne, én ad gangen eller efter præfiks, så
`select = ["E", "F"]` slår hver regel til, hvis kode begynder med E eller F, og
den særlige vælger `ALL` slår alt til. Mange regler har en rettelse med, delt op
i sikre rettelser, der skal bevare adfærden ved kørsel, og usikre, der kan ændre
den; `ruff check --fix` anvender kun den sikre slags, medmindre `--unsafe-fixes`
også er angivet.

Formatteren er bygget som en direkte erstatning for Black. På kodebaser som
Django og Zulip kommer over 99,9 % af linjerne ud identisk, men dokumentationen
siger ligeud, at de to ikke er beregnet til at blive skiftet frem og tilbage på
den samme kode. Ligesom Black springer den områder over, der er indrammet af
`# fmt: off` og `# fmt: on`.

Begge halvdele læser den samme konfiguration, som ligger under `[tool.ruff]` i
`pyproject.toml` eller i en `ruff.toml`- eller `.ruff.toml`-fil. Hver Python-fil
tjekkes mod den nærmeste konfigurationsfil over den, og den fil bruges hel: Ruff
fletter intet ind fra overliggende mapper, medmindre den nærmeste fil peger på
sin forælder med `extend`. Indstillinger på kommandolinjen går forud for alle
filer. Uden indstillinger må en linje være 88 tegn, som i Black, og den
Python-version, reglerne går ud fra, hentes fra projektets
`requires-python`-felt, når ingen konfiguration angiver en.

## Hvorfor det betyder noget

Hastighed afgør, hvor et tjek får lov at køre. En linter, der er så langsom, at
den afbryder folk, bliver skubbet ud i continuous integration, hvor den melder
sig, efter at forfatteren er gået videre. En, der svarer hurtigt, kan køre i
editoren og i en commit-hook, mens ændringen stadig er frisk.

Samling fjerner uenighed mellem værktøjer. Et projekt, der linter med Flake8 og
plugins, sorterer imports med isort og formaterer med Black, skal holde flere
konfigurationer og versionslåse i takt. Ruff giver én konfiguration, én version
at låse og én nummerering af regler, og projektet offentliggør listen over de
lintregler, der strides med dets egen formatter, så de to kan bringes til at
stemme overens med vilje.

Exitkoderne gør Ruff til en kvalitetsport uden indpakning. `ruff check`
afslutter med 0, når der ikke er nogen overtrædelser tilbage, eller alle blev
rettet, med 1, når der stadig er overtrædelser, og med 2, når konfigurationen
eller kommandolinjen er ugyldig, eller Ruff selv går ned, ligesom ESLint og
Prettier gør. `ruff format --check` afslutter med 1, når en fil ville blive
omformateret. En regel er enten valgt eller slået fra, og enhver valgt regel,
der slår ud, sætter exitkoden til 1.

Prisen er, at regelsættet er en beslutning, som nogen skal eje. Ruffs egne råd
er at liste reglerne eksplicit med `select` i stedet for at stable
`extend-select`, at starte med E og F og tilføje én gruppe ad gangen og at bruge
`ALL` med omtanke, fordi den slår nye regler til ved hver opgradering.

## Sådan ser det ud i praksis

Ruff dækkede Python i
[en zero-warnings-standard på tværs af seks sprog](/da/portfolio/set-a-zero-warnings-quality-bar-across-six-79/),
hvor hvert sprog havde sin egen linter. Hver diagnostik talte som en fejl, og
inline-undertrykkelser var forbudt, hvilket for Ruff udelukker
`# noqa`-kommentaren: et fund i Python kunne rettes i koden og på ingen anden
måde. Pre-commit- og pre-push-hooks kørte linterne og testene, så tjekket kørte,
før en commit fandtes.

Dette site giver Ruff en mindre opgave: de 22 hjælpescripts ved siden af
Hugo-buildet. [Kolofonen](/da/credits/) nævner Ruff blandt de værktøjer, der
holder kilden ærlig. Regelsættet i `pyproject.toml` er målt frem for valgt. Hver
af Ruffs 54 regelfamilier blev den 15. oktober 2026 kørt mod scriptene, 39 af
dem meldte intet, og de tavse familier, hvis emne er netop denne kode, blev
føjet til de oprindelige elleve, så udvalget nu tæller 33. Sikkerhedsfamilien,
S, er slået til med fem regler ignoreret efter kode, hver med en kommentar om,
hvorfor det mønster, der slår ud her, er den sikre tvilling til et farligt, så
familiens øvrige regler stadig kan stoppe en commit. Tre Ruff-specifikke regler
om tvetydige Unicode-tegn er ignoreret, fordi sitets tekst med vilje bruger
rigtige tankestreger og danske bogstaver.

Pre-commit-konfigurationen kører `ruff check` og `ruff format --check` på hver
Python-fil under `scripts/`, som en commit rører, og projektets fix-task kører
de samme to kommandoer i deres rettende form. Ved siden af Ruff står
basedpyright, en typechecker, med en kommentar, der trækker grænsen mellem dem:
en linter følger ikke en værdi på tværs af en funktionsgrænse, og ved sin første
kørsel fandt typecheckeren to reelle fejl, som Ruff havde godkendt.

## Hvor det går galt

Det meste besvær med Ruff kommer af en konfiguration, der er mere bogstavelig
end de mennesker, der skrev den.

**En opgradering får en urørt branch til at fejle.** Kode, der bestod i går,
melder nye overtrædelser, efter at Ruff er blevet opdateret. Den sædvanlige
årsag er `select = ["ALL"]`, der tager hver regel med, som en udgivelse
tilføjer, sammen med en version, der flyder. Lås Ruff-versionen, så en
opgradering er en commit, som nogen reviewer, og list de familier, projektet vil
have, i stedet for `ALL`, så en ny familie først kommer med, når nogen tilføjer
den.

**Én mappe holder op med at følge projektets regler.** Overtrædelser, som
rodkonfigurationen forbyder, slipper igennem i en undermappe, eller en regel,
teamet slog fra, slår ud dér. En `ruff.toml` eller en `pyproject.toml` med en
`[tool.ruff]`-tabel ligger i mappen, og fordi Ruff bruger den nærmeste fil hel,
holder alle rodindstillinger op med at gælde under den.
`ruff check --show-settings` på en fil dér udskriver stien til de indstillinger,
den endte med. Slet den indlejrede fil, eller lad den arve med
`extend = "../pyproject.toml"`.

**En undertrykkelse overlever sin grund.** En `# noqa`-kommentar kom ind af en
god grund, koden ændrede sig, og nu skjuler kommentaren ingenting eller det
næste problem på linjen. En bar `# noqa` er værre: den gør alle regler på linjen
tavse. Vælg RUF100, der melder `noqa`-kommentarer, som ikke længere undertrykker
noget, og PGH004, der kræver en bestemt kode, og lad `--fix` fjerne de døde. Her
er importen siden kommet i brug, så kommentaren beskytter ingenting, og RUF100
melder den:

```python
import json  # noqa: F401

print(json.dumps({"ok": True}))
```

**Formatteren og linteren er uenige.** `ruff format` omskriver en fil, og
`ruff check` markerer bagefter resultatet. Nogle lintregler kontrollerer det,
formatteren bestemmer, som afsluttende kommaer (COM812), indrykning (W191, E111)
og anførselstegn (Q-reglerne), og formatteren gør kun et bedste forsøg på
linjelængden, så E501 kan stadig slå ud på formateret kode. Læs de advarsler,
`ruff format` skriver ud, for en kørsel uden dem er dokumentationens egen prøve
på, at de to er enige, ignorér enhver regel, en advarsel nævner, og tag E501 som
et signal til at bygge linjen om.

**En samlet rettelse ændrer adfærd.** Efter `ruff check --fix --unsafe-fixes`
over hele træet fejler testene, eller en fejlhåndtering holder op med at fange
det, den plejede. Usikre rettelser må gerne ændre adfærden ved kørsel eller
fjerne kommentarer, og dokumentationens eget eksempel ændrer, hvilken undtagelse
et stykke kode kaster. Hold `--unsafe-fixes` ude af hooks og scripts, anvend
usikre rettelser én regel ad gangen med `--select` og læs diffen, og forfrem kun
en regel med `extend-safe-fixes`, når teamet har tjekket den.

## Læs mere

- [Ruff](https://docs.astral.sh/ruff/) — hvad Ruff erstatter, hvor hurtigt det
  hævder at være, og hvem der vedligeholder det.
- [The Ruff Linter](https://docs.astral.sh/ruff/linter/) — regelvalg og forrang,
  sikre og usikre rettelser, `noqa`-kommentarer og exitkoder.
- [The Ruff Formatter](https://docs.astral.sh/ruff/formatter/) — kompatibilitet
  med Black og de lintregler, der strides med formateringen.
- [Rules](https://docs.astral.sh/ruff/rules/) — hver regel efter kode, dens
  oprindelse, og om den kan rettes automatisk.
- [Configuring Ruff](https://docs.astral.sh/ruff/configuration/) — hvordan
  filerne findes, reglen om den nærmeste fil, `extend` og kommandolinjens
  indstillinger.

<https://engineer.company/da/glossary/ruff/>
