# Ruff

## Що це таке

Ruff — це одна програма для командного рядка, написана на Rust, яка звіряє
вихідний код Python із каталогом правил лінтингу й переписує його розкладку в
єдиний послідовний стиль. За ним стоїть Astral, компанія, що створила uv і ty, а
щоденну роботу виконують дві підкоманди: `ruff check` — це лінтер, а
`ruff format` — форматувач. Ruff заявляє, що працює в 10–100 разів швидше за
Flake8 і Black, зберігає кеш, щоб не аналізувати незмінені файли вдруге, і може
замінити Flake8 із десятками його плагінів, Black, isort, pydocstyle, pyupgrade
і autoflake.

Лінтер має понад 900 правил, і чимало з них — власні переписані версії
популярних плагінів Flake8. Кожне правило має код із короткого префікса й трьох
цифр, а префікс каже, звідки правило походить: F401, невживаний імпорт, узято з
Pyflakes, а E501, задовгий рядок, — із pycodestyle. Конфігурація обирає правила
за цими кодами, поодинці або за префіксом, тож `select = ["E", "F"]` вмикає
кожне правило, чий код починається з E чи F, а особливий селектор `ALL` вмикає
все. Багато правил мають виправлення, і їх поділено на безпечні, які мають
зберегти поведінку під час виконання, і небезпечні, які можуть її змінити;
`ruff check --fix` застосовує лише безпечні, якщо не додати ще й
`--unsafe-fixes`.

Форматувач задумано як пряму заміну Black. На таких кодових базах, як Django і
Zulip, понад 99,9% рядків виходять ідентичними, хоча документація прямо каже, що
ці два інструменти не призначені для того, щоб перемикатися між ними туди й
назад на тому самому коді. Як і Black, він пропускає ділянки між `# fmt: off` і
`# fmt: on`.

Обидві половини читають ту саму конфігурацію, яка лежить у таблиці `[tool.ruff]`
файлу `pyproject.toml` або у файлі `ruff.toml` чи `.ruff.toml`. Кожен файл
Python перевіряють за найближчим файлом конфігурації над ним, і цей файл
застосовують цілком: Ruff нічого не підмішує з батьківських тек, якщо найближчий
файл не вкаже на батьківський через `extend`. Параметри з командного рядка мають
перевагу над будь-яким файлом. Якщо нічого не задано, рядок може мати 88
символів, як у Black, а версію Python, з якої виходять правила, беруть із поля
`requires-python` проєкту, коли жодна конфігурація її не називає.

## Чому це важливо

Швидкість вирішує, де перевірці дозволять працювати. Лінтер, повільний
настільки, що перериває людину, витісняють у безперервну інтеграцію, де він
звітує вже тоді, коли автор узявся за інше. Той, що відповідає швидко, може
працювати в редакторі й у commit-хуку, поки зміна ще свіжа.

Об'єднання знімає суперечки між інструментами. Проєкт, який лінтить через Flake8
із плагінами, сортує імпорти через isort і форматує через Black, мусить тримати
узгодженими кілька конфігурацій і кілька зафіксованих версій. Ruff дає одну
конфігурацію, одну версію, яку треба зафіксувати, і одну нумерацію правил, а ще
публікує перелік правил лінтера, що конфліктують із його власним форматувачем,
щоб ці два інструменти можна було узгодити свідомо.

Коди виходу роблять Ruff перевіркою без обгортки. `ruff check` завершується з
кодом 0, коли порушень не лишилося або всі виправлено, з кодом 1, коли порушення
лишаються, і з кодом 2, коли конфігурація чи командний рядок недійсні або сам
Ruff дав збій, так само як ESLint і Prettier. `ruff format --check` завершується
з кодом 1, коли хоч один файл було б переформатовано. Правило або обране, або
вимкнене, і будь-яке обране правило, що спрацювало, ставить код виходу 1.

Ціна в тому, що набір правил — це рішення, яке хтось має взяти на себе. Власна
порада Ruff — перелічувати правила явно через `select`, а не нашаровувати
`extend-select`, починати з E і F, додавати по одній групі за раз, а `ALL`
вмикати обачно, бо він приносить нові правила з кожним оновленням.

## Як це виглядає на практиці

Ruff відповідав за Python у
[планці нуля попереджень для шести мов](/uk/portfolio/set-a-zero-warnings-quality-bar-across-six-79/),
де кожна мова мала власний лінтер. Кожна діагностика вважалася помилкою, а
inline-придушення були заборонені, і для Ruff це виключає коментар `# noqa`:
знахідку в Python можна було виправити в коді й більше ніяк. Хуки pre-commit і
pre-push запускали лінтери й тести, тож перевірка відбувалася ще до того, як
з'являвся коміт.

Цей сайт дає Ruff меншу роботу: 22 допоміжні скрипти поряд зі збіркою Hugo.
Сторінка [Подяки](/uk/credits/) називає Ruff серед інструментів, що підтримують
вихідний код у порядку. Набір правил у `pyproject.toml` виміряно, а не обрано.
Кожну з 54 родин правил Ruff прогнали на цих скриптах 15 жовтня 2026 року, 39 із
них нічого не повідомили, і ті мовчазні родини, чия тема — саме такий код,
додали до початкових одинадцяти, тож тепер обрано 33. Родина безпеки, S,
лишається ввімкненою, а п'ять її правил ігноровано за кодом, кожне з коментарем
про те, чому позначений тут шаблон є безпечним двійником небезпечного, тож решта
правил цієї родини й далі може зупинити коміт. Три специфічні для Ruff правила
про неоднозначні символи Unicode ігноровано, бо текст сайту свідомо вживає
справжні довгі й короткі тире та данські літери.

Конфігурація pre-commit запускає `ruff check` і `ruff format --check` на кожному
файлі Python у `scripts/`, якого торкається коміт, а задача `fix` у Taskfile
проєкту запускає ті самі дві команди в режимі, що виправляє. Поряд із Ruff
стоїть basedpyright, перевіряч типів, із коментарем, що проводить межу між ними:
лінтер не стежить за значенням через межу функції, і під час першого запуску
перевіряч типів знайшов два справжні дефекти, які Ruff пропустив як чисті.

## Де виникають проблеми

Здебільшого клопіт із Ruff походить від конфігурації, яка буквальніша за людей,
що її писали.

**Оновлення ламає гілку, якої ніхто не чіпав.** Код, що вчора проходив, після
оновлення Ruff повідомляє нові порушення. Звична причина — `select = ["ALL"]`,
що підхоплює кожне правило, яке додає випуск, разом із версією, яку ніхто не
зафіксував. Зафіксуйте версію Ruff, щоб оновлення було комітом, який хтось
переглядає, і перелічіть потрібні проєкту родини замість `ALL`, щоб нова родина
з'являлася лише тоді, коли її хтось додасть.

**Одна тека перестає дотримуватися правил проєкту.** Порушення, які коренева
конфігурація забороняє, проходять у підтеці, або там спрацьовує правило, яке
розробники вимкнули. У цій теці лежить `ruff.toml` чи `pyproject.toml` із
таблицею `[tool.ruff]`, і оскільки Ruff застосовує найближчий файл цілком, жодне
кореневе налаштування нижче від нього вже не діє. `ruff check --show-settings`
для файлу в цій теці друкує шлях до налаштувань, які він підхопив. Видаліть
вкладений файл або зробіть так, щоб він успадковував налаштування через
`extend = "../pyproject.toml"`.

**Придушення переживає свою причину.** Коментар `# noqa` з'явився з поважної
причини, код змінився, і тепер коментар не ховає нічого або ховає наступну
проблему в цьому рядку. Голий `# noqa` ще гірший: він заглушує всі правила в
рядку. Оберіть RUF100, що повідомляє про коментарі `noqa`, які вже нічого не
придушують, і PGH004, що вимагає конкретного коду, і дайте `--fix` прибрати
мертві. Тут імпорт відтоді почали використовувати, тож коментар нічого не
захищає, і RUF100 про нього повідомляє:

```python
import json  # noqa: F401

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

**Форматувач і лінтер розходяться.** `ruff format` переписує файл, а
`ruff check` потім позначає результат. Деякі правила лінтера стежать за тим, що
вирішує форматувач, як-от кінцеві коми (COM812), відступи (W191, E111) і стиль
лапок (правила Q), а довжини рядка форматувач дотримується лише в міру
можливого, тож E501 може спрацювати й на відформатованому коді. Читайте
попередження, які друкує `ruff format`, бо запуск без них — це власний тест
документації на те, що інструменти узгоджені, ігноруйте кожне правило, яке
називає попередження, а E501 сприймайте як сигнал перебудувати рядок.

**Масове виправлення змінює поведінку.** Після `ruff check --fix --unsafe-fixes`
по всьому дереву падають тести або обробник помилок перестає ловити те, що ловив
раніше. Небезпечним виправленням дозволено змінювати поведінку під час виконання
або прибирати коментарі, а власний приклад документації змінює, який виняток
кидає фрагмент коду. Тримайте `--unsafe-fixes` поза хуками й скриптами,
застосовуйте небезпечні виправлення по одному правилу через `--select` і читайте
diff, а правило переводьте в безпечні через `extend-safe-fixes` лише тоді, коли
розробники його перевірили.

## Що почитати далі

- [Ruff](https://docs.astral.sh/ruff/) — що Ruff замінює, наскільки швидким себе
  заявляє і хто його підтримує.
- [The Ruff Linter](https://docs.astral.sh/ruff/linter/) — вибір правил і їхній
  пріоритет, безпечні й небезпечні виправлення, коментарі `noqa` і коди виходу.
- [The Ruff Formatter](https://docs.astral.sh/ruff/formatter/) — сумісність із
  Black і правила лінтера, що конфліктують із форматуванням.
- [Rules](https://docs.astral.sh/ruff/rules/) — кожне правило за кодом, його
  походження і чи можна виправити його автоматично.
- [Configuring Ruff](https://docs.astral.sh/ruff/configuration/) — як Ruff
  знаходить файли, правило найближчого файлу, `extend` і параметри командного
  рядка.

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