TL;DR: Ruff is a Python linter and formatter written in Rust — it finds bugs, style problems, and unused code in your Python files, 10–100x faster than the old tools. Astral, the company that built Ruff and the fast package manager uv, just announced they're joining OpenAI. For vibe coders who use AI to write Python, Ruff is the easiest quality check you can add to your workflow today.
Why AI Coders Need to Know This
Python is the second most popular language AI tools generate, right behind JavaScript. When you ask Claude, Cursor, or ChatGPT to build a data script, an automation, a web scraper, or a backend API, the output is usually Python. That means Python quality problems are your quality problems — even if you didn't write a single line yourself.
Here's the uncomfortable truth: AI-generated Python is often messy. It works. It runs. But it frequently includes unused imports, inconsistent indentation, vague variable names, redundant code blocks, and occasionally logic that will fail silently under specific inputs. You won't catch these problems by reading the code — you'll catch them when something breaks in production, or when you hand it to another developer and they wince.
Ruff is a spell-checker for Python code. You run one command, it reads your entire codebase in under a second, and it tells you exactly what's wrong — and in many cases, fixes it automatically. If you build anything in Python, Ruff should be the first tool you add after learning what Python is.
And now, with Astral joining OpenAI, the tooling story is about to get a lot more interesting.
Real Scenario: AI Builds a Script, Ruff Finds the Problems
You open Claude and type:
Prompt I Would Type
Write me a Python script that:
- Reads a CSV file of customer names and emails
- Filters out any rows where the email is missing
- Sends a welcome email to each valid customer using the SendGrid API
- Logs which emails were sent and which were skipped
Keep it simple, no classes needed.
Claude delivers a working script in about 10 seconds. You test it on a small CSV, it runs fine, you move on. But before you run it in production — or before you hand it to anyone else — you run Ruff on it. Here's what Ruff finds.
What AI Generated — and What Ruff Caught
Here's a realistic version of what Claude might produce, followed by exactly what Ruff flags:
import os
import csv
import json
import requests
import sendgrid
from sendgrid.helpers.mail import Mail, Email, To, Content
from datetime import datetime
import logging
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
SENDGRID_API_KEY = os.environ.get('SENDGRID_API_KEY')
FROM_EMAIL = "hello@mycompany.com"
def send_welcome_email(customer_name, customer_email):
sg = sendgrid.SendGridAPIClient(api_key=SENDGRID_API_KEY)
from_email = Email(FROM_EMAIL)
to_email = To(customer_email)
subject = "Welcome to Our Platform"
content = Content("text/plain", f"Hi {customer_name}, welcome aboard!")
mail = Mail(from_email, to_email, subject, content)
response = sg.client.mail.send.post(request_body=mail.get())
return response
def process_customers(file_path):
sent = []
skipped = []
with open(file_path, 'r') as f:
reader = csv.DictReader(f)
for row in reader:
name = row['name']
email = row['email']
if not email:
skipped.append(name)
logger.info(f"Skipped {name} - no email")
continue
try:
response = send_welcome_email(name, email)
sent.append(email)
logger.info(f"Sent to {email}")
except Exception as e:
logger.error(f"Failed to send to {email}: {e}")
skipped.append(email)
print(f"Done. Sent: {len(sent)}, Skipped: {len(skipped)}")
return sent, skipped
process_customers("customers.csv")
Now run Ruff against it:
$ ruff check customer_emailer.py
customer_emailer.py:2:8: F401 `json` imported but unused
customer_emailer.py:4:8: F401 `requests` imported but unused
customer_emailer.py:32:20: F841 Local variable `response` is assigned but never used
customer_emailer.py:46:1: E402 Module level import not at top of file
Found 4 errors.
Four problems in 46 lines of code that appeared to work fine. Let's break down what each one means:
- F401 — unused imports:
jsonandrequestsare imported but never used. The AI grabbed them out of habit — it's seen them in thousands of Python scripts. Dead imports slow down load time slightly and, more importantly, confuse anyone reading the code later. - F841 — assigned but never used: The
responsevariable on line 32 stores the SendGrid API response, but nothing ever checks it. If the API returns an error code, your code will silently ignore it. - E402 — import not at top: This one wasn't in the code above but Ruff often catches it when AI generates scripts where some imports are inside functions or appear after executable code.
Now fix them automatically:
$ ruff check --fix customer_emailer.py
$ ruff format customer_emailer.py
Ruff removes the unused imports, flags the unused variable for you to handle manually, and standardizes all indentation, spacing, and line lengths. The whole operation takes under a second.
Understanding Each Part: Linting vs. Formatting vs. Ruff
What Is Linting?
Linting is the process of analyzing code for problems without running it. A linter reads your source code and checks it against a set of rules: unused variables, dangerous patterns, style violations, potential runtime errors. Think of it like a building inspector reviewing blueprints before construction starts — they can catch structural problems on paper before they become expensive fixes in the field.
In Python, linting has traditionally been done by tools like flake8 (style checks), pylint (more comprehensive analysis), and mypy (type checking). The problem: these tools are slow, require separate configuration files, and don't always agree with each other. Running all three on a medium-size project can take 30–60 seconds.
What Is Formatting?
Formatting is about how code looks, not what it does. Consistent formatting — indentation, line length, spacing around operators, quote style — makes code easier to read and eliminates arguments about style on teams. Python's most popular formatter has been Black, which enforces a strict, opinionated style with zero configuration. Prettier plays the same role in JavaScript.
Formatters don't catch bugs. Linters don't fix style. Historically you needed both, configured separately, and you'd have to run two commands. Ruff changes this.
Ruff Does Both — and Does Them Fast
Ruff is a single tool that replaces flake8, pylint (partially), isort (import sorting), and Black — all in one binary written in Rust. The speed difference is not marketing: Ruff checks a 100,000-line Python codebase in about 0.1 seconds. Pylint takes 40+ seconds on the same code. That's 400x faster.
Speed Comparison
Ruff vs. the old stack on a large Python project:
- pylint: ~40 seconds
- flake8: ~8 seconds
- Black: ~3 seconds
- isort: ~2 seconds
- Ruff (replaces all four): ~0.1 seconds
Why does speed matter for vibe coders? Because slow tools don't get used. If running your linter adds a 30-second pause to your workflow, you'll skip it. If it runs in under a second, you'll run it constantly — after every AI generation, before every deploy, as a pre-commit hook. Speed is the reason Ruff actually gets adopted.
For comparison, ESLint plays the same linting role in JavaScript as Ruff does in Python. Both catch common mistakes AI makes in their respective languages.
The Astral + OpenAI News: What Actually Happened
On March 19, 2026, Astral — the startup behind Ruff and uv — announced they are joining OpenAI. The announcement hit Hacker News as the #1 story of the day with 871 points. The Python community collectively lost its mind, and for good reason.
Here's the context that makes this significant:
Astral built the two most important new Python tools in years. Ruff (released 2022) became the fastest-growing Python linter ever, reaching millions of downloads per week. uv (released 2024) became a serious contender to replace pip as the default Python package manager, with adoption growing faster than any previous Python packaging tool.
OpenAI runs on Python. The entire AI ecosystem — model training, inference pipelines, API wrappers, research code — is predominantly Python. OpenAI has more reason than almost anyone to care about Python tooling being fast, reliable, and well-integrated with AI workflows.
This is a talent and tooling acquisition. Charlie Marsh, Astral's founder, built something remarkable: two critical tools with massive traction, a tiny team, and a clear vision for what Python tooling should look like. OpenAI is acquiring that vision as much as the code.
What This Means for Vibe Coders
Short term: nothing changes. Ruff and uv remain open-source. The tools will continue to work exactly as they do today. Astral has committed to keeping both projects open.
Medium term: expect tighter integration between Ruff/uv and OpenAI's developer products. Imagine Codex or the ChatGPT API automatically running Ruff on generated Python code before returning it to you. Imagine uv being the default package manager in OpenAI's sandbox environments. Imagine AI coding tools that flag Ruff violations inline as you generate code, not as an afterthought.
Long term: this signals that OpenAI is serious about owning the entire Python development experience — from model APIs to the tools you use to write code that calls those APIs. For vibe coders, that's a bet that the tools you're using to build AI apps will keep getting better and more integrated.
Also Cover: uv, the Fast Python Package Manager
Ruff gets most of the press, but uv is equally important for your workflow. If Ruff is the spell-checker for your Python code, uv is the fast-loading truck that delivers your supplies.
When you install a Python library — say, pip install pandas — pip goes out, downloads the package, figures out all its dependencies, downloads those too, and installs everything. On a fresh project with a large dependency list (like anything using PyTorch or TensorFlow), this can take 5–10 minutes.
uv does the same job in seconds. See the comparison:
# Installing the same package set:
# With pip:
$ time pip install pandas numpy matplotlib scikit-learn
... (2 minutes 14 seconds)
# With uv:
$ time uv pip install pandas numpy matplotlib scikit-learn
... (8 seconds)
uv achieves this with aggressive caching, parallel downloads, and a resolver written in Rust. It's also a drop-in replacement for pip — just swap pip for uv pip in any command you already use. Learn more about pip fundamentals at What Is pip?
uv also manages Python versions (replacing pyenv), virtual environments (replacing virtualenv), and project dependencies (replacing pip-tools). For vibe coders who just want things to work fast without deep tooling expertise, uv is the single tool that handles most of your Python environment management.
What AI Gets Wrong in Python (That Ruff Catches)
AI code generation has reliable failure modes. Knowing these lets you review AI output faster and use Ruff more strategically.
Unused Imports
This is the most common AI mistake in Python. AI models are trained on code where libraries like os, sys, json, and re appear constantly. The model pattern-matches "Python script" to "import these common libraries" regardless of whether your specific script actually uses them. Ruff's F401 rule catches every single one.
Inconsistent Formatting
AI will mix two-space and four-space indentation, use single quotes in some places and double quotes in others, and occasionally generate lines that are 150 characters long. None of this breaks your code, but it makes it harder to read and inconsistent with Python's style guide (PEP 8). ruff format normalizes everything in one pass.
Variables Assigned But Never Used
AI often generates code that stores a return value "just in case" — an API response, a file handle, a loop counter — then never references it again. These are not bugs yet, but they're clutter that obscures intent. Ruff's F841 rule flags all of them.
Bare Except Clauses
AI loves the pattern except Exception as e: pass or even just except: pass. This swallows errors silently. If something fails inside that block, you will never know. Ruff's E722 and BLE001 rules flag bare and overly broad except clauses.
# AI generates this frequently — silently hides all errors
try:
result = call_some_api()
except:
pass
# Ruff flags it. What you want instead:
try:
result = call_some_api()
except requests.exceptions.RequestException as e:
logger.error(f"API call failed: {e}")
raise
Mutable Default Arguments
This is a classic Python gotcha that trips up AI regularly. Using a mutable object (list, dict) as a default parameter value creates a shared object that persists across calls — a subtle, session-long bug that is notoriously hard to debug manually.
# AI generates this — the list is shared across ALL calls to the function
def add_item(item, items=[]):
items.append(item)
return items
# Ruff flags B006. The fix:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
For a broader look at how to catch and fix these kinds of issues, see our guide on how to debug AI-generated code.
How to Set Up Ruff
Setup takes under two minutes. Here's the full workflow.
Install Ruff
# Install via pip (works everywhere)
pip install ruff
# Or via uv (faster, recommended)
uv pip install ruff
# Or install uv first, then use it to manage everything
curl -LsSf https://astral.sh/uv/install.sh | sh
Run Your First Check
# Check all Python files in the current directory
ruff check .
# Check a specific file
ruff check my_script.py
# Check and automatically fix what can be fixed
ruff check --fix .
# Format all Python files (replaces Black)
ruff format .
Configure Ruff in pyproject.toml
Add a [tool.ruff] section to your project's pyproject.toml file to lock in your rules:
[tool.ruff]
# Target Python 3.12
target-version = "py312"
# Max line length (88 matches Black's default)
line-length = 88
[tool.ruff.lint]
# Rules to enable:
# E/W = pycodestyle errors and warnings
# F = pyflakes (unused imports, undefined names)
# B = flake8-bugbear (common bugs and design problems)
# I = isort (import sorting)
select = ["E", "W", "F", "B", "I"]
# Rules to ignore (add these as you find false positives):
ignore = ["E501"] # ignore line-too-long if you handle it elsewhere
[tool.ruff.format]
# Match Black's quote style
quote-style = "double"
Add Ruff as a Pre-Commit Hook
The most powerful way to use Ruff is as a pre-commit hook — it runs automatically every time you commit code, blocking the commit if there are unfixed errors. This means bad code never enters your git history.
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
# Install pre-commit and activate
pip install pre-commit
pre-commit install
Now ruff check runs on every git commit. If Ruff finds problems it can't auto-fix, the commit is rejected until you resolve them. It's a forcing function that makes quality non-optional.
Use Ruff in VS Code
Install the official Ruff extension from the VS Code marketplace. It shows inline warnings as you type, underlines problems in red, and offers one-click auto-fixes. Combined with GitHub Copilot or Cursor, you get real-time feedback on AI-generated code before you even save the file.
What to Learn Next
Ruff is one piece of a healthy Python development workflow. Build out the rest:
- What Is Python? — if you're new to the language Ruff is linting.
- What Is pip? — Python's default package manager, and why uv is replacing it.
- What Is ESLint? — Ruff's JavaScript equivalent, for when AI builds in JS.
- What Is Prettier? — the JS/TS formatter that plays the same role as
ruff format. - How to Debug AI-Generated Code — what to do when Ruff flags something it can't auto-fix.
- What Is Git? — Ruff's pre-commit hooks are git-powered. Understand the version control system that makes automated linting possible.
- What Is GitHub? — run Ruff in GitHub Actions CI so every pull request gets checked automatically before merge.
Next Step
Open any Python script AI has generated for you and run ruff check --fix . right now. You'll probably find at least 2–3 issues. It takes 30 seconds and immediately makes the code more trustworthy.
FAQ
Ruff is a tool that reads your Python code and flags problems — unused imports, bad formatting, potential bugs, style violations — before you run it. Think of it as a spell-checker for code. It is written in Rust, which makes it 10 to 100 times faster than older Python linters like flake8 or pylint.
Astral built two of the most important Python developer tools in recent years: Ruff (a linter and formatter) and uv (a fast package manager). OpenAI uses Python heavily and is betting that tighter integration between AI coding tools and developer tooling will improve how people build with AI. The acquisition gives OpenAI direct influence over the Python tooling ecosystem.
Yes, especially because AI writes your code. AI-generated Python often has unused imports, inconsistent formatting, vague variable names, and occasional type mismatches. Ruff catches all of these in under a second. Running ruff check . after every AI generation is a fast, low-effort quality gate.
Black is a formatter only — it fixes how your code looks (indentation, spacing, line length). Ruff does formatting AND linting. It can replace both Black and flake8 in a single tool. Ruff's formatter is designed to be compatible with Black's output, so if your team uses Black, switching to Ruff format is straightforward.
uv is a Python package manager built by the same company (Astral) that built Ruff. Where pip installs packages slowly, uv does the same job 10 to 100 times faster because it is also written in Rust. They are separate tools that solve different problems, but both come from the same mission: make Python tooling dramatically faster.