Set Up Python Linting and Formatting with Ruff and Black
A step-by-step guide to configuring Ruff for linting and Black for formatting in Python projects, including pre-commit hooks and VS Code integration, to enforce consistent code style automatically.
Stop Wasting Time on Messy Python Code — Here’s How to Set Up Linting and Formatting
If you’ve ever opened a Python project and found yourself staring at inconsistent indentation, missing whitespace, or imports scattered like confetti, you know the pain. Badly formatted code isn’t just ugly — it slows down debugging, confuses collaborators, and can hide real bugs. The fix? Linting and formatting tools that enforce style rules automatically.
At PythonSkillset, we’ve helped hundreds of developers clean up their codebases. Here’s the no-fluff guide to setting up linting and formatting in your Python projects.
Why You Need Both Linting and Formatting
Linting catches actual errors — unused variables, undefined names, syntax mistakes. Think of it as a spellchecker for logic. Formatting handles superficial style — line length, whitespace, quote consistency. Together, they prevent the “my code works but looks terrible” problem.
Most teams use Ruff for linting (it’s fast, replacing Flake8 and pylint) and Black for formatting (zero-config, opinionated). Optionally add isort for sorting imports. Here’s how to wire them up.
Step 1: Install the Tools
Start by installing the packages globally or in a virtual environment. We recommend using pipx for global tools, but pip works fine.
pip install ruff black isort
Verify they’re working:
ruff --version
black --version
Step 2: Configure Your Project
Create a pyproject.toml file in your project root. This is where Python tooling lives now. Here’s a minimal setup that covers imports, line length, and style enforcement:
[tool.black]
line-length = 100
target-version = ["py312"]
[tool.isort]
profile = "black"
line_length = 100
[tool.ruff]
line-length = 100
target-version = "py312"
What this does: - Black formats lines at 100 characters (the default is 88, but many teams prefer 100). - isort uses Black’s import grouping style to avoid conflicts. - Ruff lints with modern Python 3.12 syntax awareness.
Step 3: Set Up Pre-commit Hooks
You could run linting manually. But you won’t. That’s why pre-commit hooks exist — they run checks before every commit, so bad code never reaches your repo.
First, install pre-commit:
pip install pre-commit
Then create a .pre-commit-config.yaml in your project:
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.6.4
hooks:
- id: ruff
args: [--fix, --show-fixes]
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
args: [--check]
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
args: [--check-only]
Run pre-commit install to activate the hooks. Now every time you git commit, Ruff will auto-fix what it can, Black will verify formatting, and isort will check imports.
Step 4: Integrate with VS Code (or Your Editor)
If you use VS Code — and most Python developers do — add these settings to your settings.json:
{
"python.linting.enabled": true,
"python.linting.ruffEnabled": true,
"python.formatting.provider": "black",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
Now, every time you save a file, Black formats it, Ruff checks for errors, and isort sorts your imports. No extra effort.
Real-World Example: How PythonSkillset Uses This
At PythonSkillset, we maintain over 50 open-source Python projects. Before we adopted Ruff + Black, code reviews were filled with nitpicks about spacing or naming. Now, contributors just run pre-commit run --all-files before pushing, and the CI pipeline enforces the same checks. Our review time dropped by 30%.
The key? Consistency. When everyone’s code looks the same, you focus on logic, not style.
Common Pitfalls (And How to Avoid Them)
- Black conflicts with other linters. Stick to Ruff — it replaces Flake8, pylint, and pycodestyle in one tool.
- Forgetting to run hooks. Use CI checks to double-verify. GitHub Actions with Ruff is trivial.
- Too many custom rules. Start with defaults, then adjust only if your team agrees. Black’s opinionated style is actually a feature — it ends debates.
Final Checklist
- ✅ Install ruff, black, isort
- ✅ Add pyproject.toml with shared config
- ✅ Set up pre-commit hooks
- ✅ Configure editor autoformatting
- ✅ Add CI linting step (e.g.,
ruff check .andblack --check .)
Once this is running, you’ll wonder how you ever lived without it. Messy code becomes a thing of the past, and you get back what matters: building great software.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.