Menjalankan flake8, black, dan isort satu per satu di proyek Python itu lambat dan menyebalkan — apalagi saat konfigurasinya saling tabrakan. Ruff memangkas semuanya jadi satu binary. Ditulis dalam Rust, Ruff memeriksa ratusan rule dalam hitungan milidetik dan memformat kode dengan gaya yang kompatibel dengan black. Saya sudah memakainya di semua proyek sejak 2024 dan tidak pernah menoleh ke belakang. Mulai dari instalasi, lalu kita atur konfigurasinya di pyproject.toml.

Kenapa Satu Tool Bisa Menggantikan Tiga

Ruff menggabungkan peran flake8 (linter), black (formatter), isort (pengurutan import), dan pyupgrade (upgrade sintaks lama) ke dalam satu binary. Keuntungan terbesarnya bukan cuma kecepatan — kamu tidak perlu lagi menyinkronkan konfigurasi antar-tool atau menyelesaikan konflik versi dependensi. Ruff juga membawa aturan bawaan dari pyflakes, pycodestyle, pydocstyle, pylint, dan mccabe, jadi coverage-nya jauh lebih luas daripada flake8 standar.

Instalasi

Ruff ada di PyPI dan bisa dipasang dengan pip biasa. Kalau kamu sudah pakai uv untuk manajemen dependensi, cukup satu perintah:

bash
uv add --dev ruff

Atau lewat pip biasa:

bash
pip install ruff

Cek versinya untuk memastikan sudah terpasang:

bash
ruff --version

Lint dan Format Pertama Kali

Buka folder proyek Python mana pun, lalu jalankan:

bash
ruff check .

Perintah ini memindai seluruh file .py di direktori saat ini. Default-nya Ruff mengaktifkan rule E (pycodestyle errors) dan F (pyflakes), jadi kamu akan melihat daftar masalah seperti import tidak terpakai, variabel yang tidak pernah dibaca, atau error sintaks. Outputnya mirip flake8, tapi muncul jauh lebih cepat.

Untuk memformat kode, pakai:

bash
ruff format .

Ruff format menggunakan gaya yang kompatibel dengan black. Kalau kamu bermigrasi dari black, hasilnya hampir identik — kode yang sudah diformat black tidak akan berubah lagi saat dilewatkan ke ruff format.

Konfigurasi di pyproject.toml

Semua pengaturan Ruff ditaruh di pyproject.toml, di bawah bagian [tool.ruff]. Ini contoh konfigurasi yang saya pakai di proyek nyata:

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

[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "UP", "B", "SIM"]
ignore = ["E501"]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"]

Baris select menentukan kelompok rule yang aktif. E dan F adalah default, I mengurutkan import (peran isort), N mengawasi konvensi penamaan, UP mengusulkan upgrade sintaks, B menangkap bug umum, dan SIM menyederhanakan kode. Baris ignore = ["E501"] mematikan peringatan baris terlalu panjang, karena panjang baris sudah ditangani formatter.

Mulai dari select yang kecil, lalu tambah kelompok rule secara bertahap. Mengaktifkan semua rule sekaligus di proyek lama akan menghasilkan ratusan peringatan dan bikin kamu menyerah sebelum selesai.

Perbaikan Otomatis dengan --fix

Sebagian besar masalah yang ditemukan ruff check bisa diperbaiki otomatis. Tambahkan flag --fix:

bash
ruff check --fix .

Ruff membedakan fix yang aman dan yang berisiko. Fix aman tidak mengubah perilaku program — contohnya menghapus import tak terpakai atau mengurutkan import. Fix berisiko bisa mengubah hasil, misalnya mengubah == menjadi is saat membandingkan dengan None.

Jalankan ruff check --fix --unsafe-fixes hanya kalau kamu siap meninjau ulang diff-nya. Fix berisiko kadang mengubah logika, jadi selalu cek lewat git diff sebelum commit.

Kalau kamu hanya ingin melihat apa yang akan diubah tanpa benar-benar mengubahnya, gunakan --diff:

bash
ruff check . --fix --diff

Integrasi VS Code

Pasang ekstensi Ruff dari marketplace VS Code, lalu tambahkan ini ke settings.json supaya format dan perbaikan berjalan otomatis saat save:

json
{
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll.ruff": "explicit",
      "source.organizeImports.ruff": "explicit"
    }
  }
}

Setelah itu, setiap kamu menyimpan file Python, import terurut dan masalah ringan langsung beres tanpa perlu menjalankan perintah di terminal.

Pre-commit Hook

Ruff paling terasa manfaatnya saat dipasang sebagai pre-commit hook, jadi setiap commit otomatis bersih. Kalau kamu belum akrab dengan Git, baca dulu panduan Git untuk pemula. Tambahkan ini ke .pre-commit-config.yaml:

python
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.9.0
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

Lalu instal hook-nya dengan pre-commit install. Sekarang setiap git commit akan menolak kode yang masih kotor sampai Ruff membenahinya.

Menjalankan Ruff di CI (GitHub Actions)

Supaya semua anggota tim terpaksa mematuhi aturan, jalankan Ruff di pipeline CI. Kalau CI/CD masih terasa asing, saya sudah menulis tutorial GitHub Actions dari nol. Berikut workflow minimal:

python
name: Lint

on: [push, pull_request]

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/ruff-action@v3
        with:
          args: "check --output-format=github"

Workflow ini memblokir merge saat ada rule yang dilanggar. Output berformat github membuat peringatan muncul langsung di tab Files Changed pada pull request.

Tanya Jawab Singkat

Ruff format bedanya apa dengan black?

Hasilnya kompatibel. Ruff menargetkan output yang sama dengan black, tapi prosesnya jauh lebih cepat karena ditulis dalam Rust. Kalau kamu bermigrasi dari black, mayoritas file tidak akan berubah sama sekali.

Apakah Ruff bisa menggantikan semua plugin flake8?

Sebagian besar plugin populer seperti flake8-bugbear, flake8-simplify, dan pyupgrade sudah diimplementasikan langsung di Ruff sebagai kelompok rule. Cek dokumentasi resmi untuk daftar lengkap rule yang didukung.

Kenapa target-version penting?

Nilai ini memberi tahu Ruff versi Python minimum proyek kamu, supaya rule upgrade sintaks (UP) tidak menyarankan fitur yang belum tersedia di versi tersebut.

Langkah paling cepat untuk merasakan efeknya: jalankan ruff check --fix di satu repo lama yang masih kamu rawat, lalu lihat berapa banyak masalah yang beres dalam satu perintah. Kalau hasilnya memuaskan, pasang pre-commit-nya supaya setiap commit berikutnya sudah bersih dari awal.