Skip to content

Code Formatting with Ruff

Ruff is the modern, fast code formatter that replaces Black and more. It's 100x faster and requires zero configuration to get started.

Why Ruff Format?

Key Benefits

  • 100x faster than Black
  • Black-compatible output
  • Zero configuration needed
  • Single tool for formatting and linting
  • Written in Rust for maximum performance

Installation

Ruff is typically installed as a development dependency:

# With uv
uv pip install ruff

# Or add to requirements/dev.in
ruff>=0.8.0

Quick Start

Format Code

# Format all Python files
ruff format .

# Format specific file
ruff format myfile.py

# Check what would change (dry run)
ruff format --check .

# Format and show diff
ruff format --diff .

Basic Configuration

Create pyproject.toml:

[tool.ruff]
# Line length to match your team's preference
line-length = 180

# Python version
target-version = "py313"

# Exclude directories
exclude = [
    ".git",
    ".venv",
    "__pycache__",
    "migrations",
    "node_modules",
]

That's it! Ruff works great with minimal configuration.

Configuration Options

Line Length

[tool.ruff]
# Default: 88 (Black-compatible)
line-length = 180

# Our team uses 180 for Django projects
# Adjust based on your team's preference

Recommendation: 120-180 characters for modern development. 88 is conservative.

Quote Style

[tool.ruff.format]
# Options: "double" (default), "single"
quote-style = "double"

# Prefer double quotes for consistency

Indentation

[tool.ruff.format]
# Options: "space" (default), "tab"
indent-style = "space"

# Always use spaces (PEP 8)

Magic Trailing Comma

[tool.ruff.format]
# Respect trailing commas
skip-magic-trailing-comma = false

Integration with Editors

VS Code

Install the Ruff extension:

  1. Open Extensions (Ctrl+Shift+X)
  2. Search for "Ruff"
  3. Install official Ruff extension

.vscode/settings.json:

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

PyCharm / IntelliJ

  1. Go to Settings → Tools → External Tools
  2. Add new tool:
  3. Name: Ruff Format
  4. Program: ruff
  5. Arguments: format $FilePath$
  6. Working directory: $ProjectFileDir$

  7. Optionally bind to keyboard shortcut

Vim/Neovim

With null-ls:

local null_ls = require("null-ls")

null_ls.setup({
    sources = {
        null_ls.builtins.formatting.ruff,
    },
})

Emacs

(use-package reformatter
  :config
  (reformatter-define ruff-format
    :program "ruff"
    :args '("format" "--stdin-filename" buffer-file-name "-")
    :lighter " RuffFmt"))

(add-hook 'python-mode-hook 'ruff-format-on-save-mode)

Pre-commit Integration

.pre-commit-config.yaml:

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.11.8
    hooks:
      # Run formatter
      - id: ruff-format

      # Run linter with auto-fix
      - id: ruff
        args: [--fix]

This runs Ruff format and lint on every commit.

CI/CD Integration

GitHub Actions

name: Lint

on: [push, pull_request]

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Ruff
        run: pip install ruff

      - name: Check formatting
        run: ruff format --check .

      - name: Run linter
        run: ruff check .

GitLab CI

ruff:
  image: python:3.13-slim
  script:
    - pip install ruff
    - ruff format --check .
    - ruff check .

justfile Integration

justfile:

# Format all code
format:
    ruff format .

# Check formatting without changes
format-check:
    ruff format --check .

# Format and lint
lint: format
    ruff check --fix .

# Pre-commit check
check: format-check
    ruff check .
    pytest

Formatting Rules

Import Sorting

Ruff automatically sorts imports:

# Before
from myapp.models import User
import sys
from django.conf import settings
import os

# After (Ruff formatted)
import os
import sys

from django.conf import settings

from myapp.models import User

Order: stdlib → third-party → first-party

String Quotes

# Ruff uses double quotes by default
name = "Bob"
description = "A long string"

# But respects your choice for docstrings
def example():
    """Docstrings use triple double quotes."""
    pass

Line Breaking

# Ruff handles long lines intelligently

# Before
result = some_very_long_function_name(argument1, argument2, argument3, argument4, argument5)

# After
result = some_very_long_function_name(
    argument1,
    argument2,
    argument3,
    argument4,
    argument5,
)

Dictionary/List Formatting

# Short: stays on one line
data = {"name": "Bob", "age": 30}

# Long: breaks intelligently
data = {
    "name": "Bob",
    "age": 30,
    "email": "bob@example.com",
    "address": "123 Main St",
}

Differences from Black

Ruff format is designed to be Black-compatible, but has some differences:

Similarities

  • Same default line length (88)
  • Same string quote preference (double)
  • Same import sorting behavior
  • Compatible with Black-formatted code

Differences

  • Much faster (100x)
  • Integrated with linter (one tool)
  • More configurable (if needed)
  • Better performance on large codebases

Migration from Black

No code changes needed! Just replace:

# Old
black .

# New
ruff format .

Update your configs:

# pyproject.toml
# Remove [tool.black] section
# Add [tool.ruff] if needed

Common Patterns

Disable Formatting for Block

# fmt: off
data = [
    1,    2,    3,
    4,    5,    6,
    7,    8,    9,
]
# fmt: on

Use sparingly! Usually Ruff's formatting is better.

Long Strings

# Ruff handles long strings well

message = (
    "This is a very long message that needs to be split "
    "across multiple lines for better readability."
)

# Or use triple quotes
message = """
This is a multi-line string
that preserves formatting.
"""

Function Signatures

# Short
def simple(a, b):
    return a + b

# Long - Ruff breaks intelligently
def complex_function(
    argument_one: str,
    argument_two: int,
    argument_three: Optional[dict] = None,
) -> dict:
    """Ruff formats this nicely."""
    pass

Best Practices

✅ Do

  • Run on save in your editor
  • Use pre-commit hooks for consistency
  • Format before committing always
  • Trust Ruff - don't fight the formatter
  • Consistent line length across team
  • CI enforcement to prevent unformatted code

❌ Don't

  • Don't manually format code
  • Don't disable formatting without good reason
  • Don't mix formatters (Black + Ruff)
  • Don't skip formatting in CI
  • Don't use # fmt: off excessively

Performance

Speed Comparison

Operation Black Ruff Improvement
Format 100 files 1.2s 0.01s 120x faster
Format large project 5.0s 0.05s 100x faster
Check formatting 0.8s 0.008s 100x faster

These improvements are noticeable in development and CI/CD.

Troubleshooting

Formatting Not Applied

# Check if file is excluded
ruff format --check myfile.py --verbose

# Check config
cat pyproject.toml | grep -A 10 "\[tool.ruff\]"

Conflicts with Editor

# Disable other formatters in editor
# Keep only Ruff

# VS Code: Check settings.json
# PyCharm: Disable Black plugin

CI Failures

# Run locally to see failures
ruff format --check .

# See what would change
ruff format --diff .

# Fix locally
ruff format .

Advanced Configuration

Per-File Formatting

[tool.ruff.format]
# Format specific files differently (rarely needed)

# Example: Different line length for generated files
[[tool.ruff.overrides]]
files = ["generated/*.py"]
line-length = 120

Respecting gitignore

[tool.ruff]
# Respect .gitignore patterns (default: true)
respect-gitignore = true

Preview Features

[tool.ruff.format]
# Enable unstable features (use with caution)
preview = false

Comparison: Alternatives

Black

  • Pro: Widely adopted, stable
  • Con: Slower, separate tool
  • Verdict: Ruff format is better (Black-compatible + faster)

YAPF

  • Pro: Very configurable
  • Con: Too much configuration, slower
  • Verdict: Ruff format is simpler

autopep8

  • Pro: PEP 8 focused
  • Con: Limited formatting, outdated
  • Verdict: Ruff format is more modern

Recommendation: Use Ruff format for all new projects.

Team Adoption

Gradual Rollout

  1. Week 1: Install Ruff, run ruff format --check .
  2. Week 2: Format codebase, commit: ruff format .
  3. Week 3: Add pre-commit hooks
  4. Week 4: Enforce in CI

Team Agreement

Document in team README:

## Code Formatting

We use Ruff for code formatting.

- Line length: 180 characters
- Run `just format` before committing
- Pre-commit hooks will auto-format
- CI will reject unformatted code

Further Reading

Next Steps