Phase 2: Static Analysis & Automated Gates · 50 min · Python · Ruff · mypy
Linting & Static Analysis Basics
The linter never trusts the AI. Neither should you.
Hiring signal: Engineers who can configure strict linters that catch AI-specific anti-patterns (unused variables, repetitive logic, missing type annotations) demonstrate the ability to build automated quality gates that scale across 75+ AI-assisted PRs per day.
What you will learn
- Configure Ruff with strict rulesets that catch AI-introduced anti-patterns
- Set up mypy in strict mode to enforce type safety on AI-generated code
- Integrate linters into pre-commit hooks for real-time feedback
- Measure and interpret alert density differences between AI and human code
The Problem
An AI generates this Python function:
def process_data(data, config, options, flags, settings, params, env, ctx):
result = data
temp = config
temp = options
temp = settings
output = result
return output
A human reviewer might not notice: temp is assigned three times but never read (variable assignment error -- the #1 AI defect pattern). flags, params, env, and ctx are unused parameters. output is just an alias for result. No type annotations. No docstring.
Ruff catches all of these in 0.2 seconds. mypy catches the missing types. Together, they form the first layer of defense against AI code defects -- and the data shows AI code needs it more than human code: AI code triggers 12.81 alerts per KLOC vs 11.58 for human code, with significantly more high-risk alerts (0.934 vs 0.464 per KLOC).
Ruff: The Fast First Line of Defense
Ruff is a Python linter written in Rust that runs 10-100x faster than flake8. It catches:
| Rule Category | What it catches | AI code relevance |
|---|
F (pyflakes) | Unused variables, undefined names, redefined names | Catches AI variable assignment errors |
E (pycodestyle) | Style violations, whitespace | Less relevant for AI code |
W (warnings) | Deprecation warnings | Catches AI using deprecated APIs |
I (isort) | Import ordering | Catches AI hallucinated imports |
B (bugbear) | Common Python pitfalls | Catches AI anti-patterns |
UP (pyupgrade) | Python version upgrades | Catches AI using old syntax |
SIM (simplify) | Code simplification | Catches AI repetitive logic |
S (bandit) | Security issues | Catches AI security anti-patterns |
Strict Ruff configuration for AI code
# pyproject.toml
[tool.ruff]
target-version = "py311"
line-length = 100
[tool.ruff.lint]
select = [
"F", # pyflakes - catches unused vars (AI defect #1)
"E", # pycodestyle errors
"W", # pycodestyle warnings
"I", # isort - catches hallucinated imports
"B", # bugbear - catches AI anti-patterns
"UP", # pyupgrade - catches old syntax
"SIM", # simplify - catches repetitive logic
"S", # bandit - catches security issues
"C4", # comprehensions - catches AI verbose loops
"PIE", # misc - catches AI unnecessary code
"RET", # return - catches AI inconsistent returns
"PT", # pytest style - catches AI test anti-patterns
]
ignore = ["E501"] # line length handled by formatter
[tool.ruff.lint.per-file-ignores]
"tests/*" = ["S101"] # assert is OK in tests
Ruff catches AI defect patterns automatically
The F (pyflakes) ruleset catches the #1 AI defect pattern: variable assignment errors. When an AI assigns a variable but overwrites it before reading, Ruff flags it as F811 (redefined-while-unused) or F841 (unused-variable). The SIM ruleset catches the #2 AI defect pattern: repetitive logic. When an AI duplicates code blocks, Ruff flags it as SIM102 or SIM118. This is why strict linting is the first line of defense -- it catches structural defects that unit tests miss because the code "works" even though it's wrong.
Unlock the full lesson
You've read the first 2 sections. The rest of this lesson covers mypy: Type Safety for AI Code, Pre-Commit Hooks: Real-Time Linting in the AI Loop, Build It — plus a hands-on lab, quiz, and project artifact.
Create a free account to unlock Phase 0 and Phase 1 of every course — no credit card.
Browse all courses · View pricing · DeVenture Academy