Skip to content
intermediate Phase 12 · Python Type Hints & Quality

Linting & Formatting

Enforce code quality with ruff, black, isort, and pre-commit hooks.

45m
0 problems
Topic Progress 0%

Tool Roles: Lint, Format, Sort

Tool Roles: Lint, Format, Sort

Three jobs, three concerns — one tool can cover them:

Job Tool What it fixes
Linting ruff Unused imports, undefined names, bug patterns (flake8-style rules in Rust, fast)
Formatting black Canonical layout: 88-char default line length, trailing commas, normalized strings
Import order isort or ruff I rules Groups stdlib / third-party / local, alphabetical within groups

ruff replaces flake8, isort, pyupgrade, and several plugins in one binary. If you keep standalone isort, set profile = "black" so its splits match black's wrapping — otherwise the two tools fight over every file.

Configuration and Hooks

Configuration and Hooks

Single source of truth in pyproject.toml:

[tool.black]
line-length = 88

[tool.ruff]
line-length = 88            # must match black

[tool.ruff.lint]
select = ["E", "F", "I", "UP"]   # pycodestyle, pyflakes, isort, pyupgrade
ignore = ["E501"]                 # black owns line length

Then pre-commit-config.yaml:

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.8.4
    hooks: [{ id: ruff }, { id: ruff-format }]

Run pre-commit install once per clone, and pre-commit run --all-files after changing hook revisions so already-committed files get migrated too.

Pitfalls and Interview Angles

Pitfalls and Interview Angles

Local passes, CI fails. Hooks only touch staged files; history that predates the config is unchecked until pre-commit run --all-files or a one-shot ruff check --fix + black ..

Formatter churn. Pin tool versions in the hook config — upgrading black mid-project rewrites hundreds of lines; do that as its own commit.

CI gates. Use check modes, not mutators: black --check ., ruff check ., mypy src. Auto-fixing in CI creates noise and hides that developers ran different versions locally.

Interview angle: explain why line length belongs to the formatter, linting to the linter, and why splitting rules prevents two tools from editing the same line repeatedly.

Quiz

1. Why set isort profile = "black" when black formats the project?

Question 1 options

2. Which command belongs in CI to enforce formatting?

Question 2 options

3. Why must [tool.ruff] line-length match black’s?

Question 3 options

Flashcards

Question

ruff vs flake8

Answer

Same rule families (E/F), written in Rust — orders of magnitude faster, plus I (isort) and UP (pyupgrade) rules.

Question

black defaults

Answer

88-character lines, opinionated, no config beyond line-length/target-version.

Question

pre-commit run --all-files

Answer

Applies hooks to every tracked file — required after installing or upgrading hooks.

Question

CI style gates

Answer

black --check, ruff check, mypy: verify only, never mutate in CI.

Revision Notes

Key Takeaways

  • 1. Linting finds bugs, formatting removes style debate, import sorting stays deterministic
  • 2. Keep one shared line-length in pyproject.toml so tools never fight
  • 3. Pin hook versions and enforce check-only commands in CI

Interview Tips

  • • Explain the division of labor (formatter owns layout, linter owns correctness) rather than naming tools only
  • • Describe migrating an existing repo: one formatting commit, then hooks plus CI to keep it clean

Cheat Sheet

ruff (lint+I+UP) / black (len 88) / isort profile="black"; pyproject [tool.ruff.lint] select+ignore; pre-commit run --all-files; CI: black --check, ruff check.

Continue Learning