Skip to content
Try CosmosGet Started
Back to Guides

Python Code Review Checklist: 25 Things to Check in 2026

Jan 16, 2026Last updated: Aug 20, 2026
Molisha Shah
Molisha Shah
Python Code Review Checklist: 25 Things to Check in 2026

An effective Python code review checklist in 2026 is a set of 25 prioritized pass/fail checks spanning style, types, tests, security, and AI-generated code, because reviews without explicit criteria default to preference debates, while injection flaws and hallucinated dependencies reach production.

TL;DR

Python code reviews fail when teams lack shared pass/fail criteria: reviewers debate formatting while AI-generated code introduces OWASP Top 10 vulnerabilities at measurable rates. This checklist organizes 25 concrete checks by priority, updated for Ruff, uv, mypy 2.x, OWASP Top 10:2025, and reviewing AI-generated Python.

Teams keep relitigating the same style choices while genuine risks slip through. The fix is explicit pass/fail criteria. Google's engineering practices set the bar: reviewers should favor approving a CL once it clearly improves the system's overall code health, even if it is not perfect.

The 2026 revision of this checklist reflects three shifts. Ruff has consolidated Black, isort, and Flake8 into one tool. Python 3.9 reached end of life, which moves the modern type-hint syntax baseline to every supported Python version. And AI assistants now write a large share of the code under review, which demands its own checks. Teams formalizing these criteria alongside their enterprise coding standards can automate most of the list.

The limitation is that per-file linters and formatters enforce syntax rules without seeing how modules interact: a type mismatch across a service boundary or a hardcoded credential referenced from three call sites stays invisible until production. Augment Cosmos, the unified cloud agents platform, closes that blind spot through Context Engine, which reads entire codebases and catches cross-file issues before human review begins.

The 25-Point Python Code Review Checklist

Every check below is a pass/fail criterion a reviewer can apply in one read. Detailed criteria, tool commands, and code examples follow in the sections after this list.

  1. Style and formatting (must-fix): 4-space indentation, no mixed tabs; lines ≤88 characters (Ruff default) or ≤79 (strict PEP 8)
  2. snake_case functions and variables, PascalCase classes, UPPER_CASE constants
  3. Imports in three sorted groups (stdlib, third-party, local), enforced by Ruff I rules
  4. .pre-commit-config.yaml present with ruff-check, ruff-format, and Bandit hooks
  5. Tests and type checking (must-fix): ≥80% coverage on new or modified code (--cov-fail-under=80)
  6. Modern type-hint syntax (list[str], X | None); mypy strict mode in CI
  7. Specific exception types only; no bare except; tracebacks logged
  8. Assertions validate exact expected values; pytest.raises(match=) on error messages (recommended)
  9. Tests run independently in any order with no shared mutable state (recommended)
  10. Fixture scope matches setup cost (session for expensive, function for isolation) (recommended)
  11. Readability and structure (mixed priority): Cyclomatic complexity ≤10 per function (high)
  12. Functions fit on one screen (40-60 lines) with a single responsibility (high)
  13. Context managers (with) for files, connections, and locks (must-fix)
  14. Idiomatic patterns: no mutable default arguments, enumerate() over manual counters, minimal try-block scope (must-fix)
  15. Performance (mixed, profile first): O(1) set/dict membership tests; no O(n²) patterns inside loops (high, context-dependent)
  16. Generators for streaming large data; __slots__ on high-instance-count classes (recommended)
  17. Security (must-fix, OWASP Top 10:2025 mapped): Parameterized SQL queries only (A05:2025)
  18. No subprocess with shell=True on user input; no os.system (A05:2025)
  19. No pickle.loads or yaml.load on untrusted data (A08:2025)
  20. No hardcoded secrets; credentials from environment or a secrets manager (A02:2025) (critical)
  21. Allowlist validation on user-supplied URLs to block SSRF (A01:2025)
  22. No MD5/SHA1 password hashing, random for tokens, or verify=False (A04:2025)
  23. pip-audit in CI; HIGH/CRITICAL CVEs block merge (A03:2025)
  24. Documentation and dependencies (must-fix): Google-style docstrings with Args/Returns/Raises on all public APIs
  25. Committed lock file with exact versions and hashes; written justification for new dependencies

Style and PEP 8 Compliance (Checks 1-4)

Style checks belong to tooling, not humans. Ruff now replaces Flake8, Black, isort, pydocstyle, and pyupgrade in a single binary, and its formatter matches Black output on more than 99.9% of lines in projects like Django and Zulip. Automating checks 1-3 lets reviewers focus on logic and architecture.

1. Indentation and Line Length

Priority: Must-fix. Pass: 4 spaces per indentation level, no mixed tabs and spaces, lines ≤88 characters (Ruff default) or ≤79 according to PEP 8. PEP 8 permits teams to agree on up to 99 characters, with docstrings and comments still wrapped at 72.

2. Naming Conventions

Priority: Must-fix. Pass: Functions and variables use snake_case, classes use PascalCase, constants use UPPER_CASE. Names are the first layer of documentation a reader encounters; consistent casing communicates scope and type without a lookup.

3. Import Organization

Priority: Must-fix. Pass: Three groups separated by blank lines (standard library, third-party, local), sorted within each group. A standalone isort install is no longer needed; use Ruff's I rule prefix instead.

4. Pre-Commit Hook Enforcement

Priority: Must-fix. Pass: Repository includes a .pre-commit-config.yaml with formatter, linter, and security hooks. The 2026-current configuration:

yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.2
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: check-yaml
- repo: https://github.com/PyCQA/bandit
rev: 1.9.4
hooks:
- id: bandit
args: ["-c", "pyproject.toml"]
additional_dependencies: ["bandit[toml]"]

Two ordering notes from the ruff-pre-commit docs: ruff-check with --fix must run before ruff-format, since fixes may output code that needs reformatting. And Ruff 0.16.0 changed the defaults substantially (413 rules are active by default, 18 previously default E/F rules were removed), so teams upgrading from 0.15.x should audit their select list. Teams wanting faster hook execution can swap the runner for prek, a Rust reimplementation that reads the same config file unchanged and is used by CPython, FastAPI, and Ruff itself.

Test Coverage and Type Checking (Checks 5-10)

Tests and types are the primary defense against regressions, and both toolchains changed under teams' feet in the last year. mypy 2.0 shifted defaults, pytest 9 added native TOML config, and Python 3.9's end of life reset the type-syntax baseline.

5. Test Coverage Threshold

Priority: Must-fix. Pass: ≥80% coverage on changed code, with branch = true under [tool.coverage.run] if the team wants branch counting, and with edge cases, error paths, and boundary conditions tested according to unit testing practices that weigh quality over raw percentage. Configure addopts = "--cov=src --cov-fail-under=80" in pyproject.toml. Note that pytest-cov 7.1.0 fixed total-coverage computation so --cov-fail-under behaves consistently regardless of reporting options; earlier versions could pass or fail the same code depending on report settings.

6. Type Hints With the 2026 Baseline

Priority: Must-fix. Pass: list[str] instead of List[str] (PEP 585), X | None instead of Optional[X] (PEP 604), mypy running in CI with strict mode. Python 3.9 reached end of life in October 2025, so all supported versions now accept both syntaxes. PEP 695 generics (class Foo[T]:, the type statement) require 3.12+; flag them in projects still supporting 3.10 or 3.11.

python
# ❌ FAIL - Deprecated typing imports
from typing import List, Dict, Optional
def process(items: List[str]) -> Optional[Dict[str, int]]:
pass
# ✅ PASS - Baseline syntax for all supported versions
def process(items: list[str]) -> dict[str, int] | None:
pass

Set strict = true under [tool.mypy] in mypy's config. Teams upgrading from mypy 1.x should expect new failures under 2.x: --strict-bytes and --local-partial-types are now default, and bytearray is no longer treated as a subtype of bytes. mypy remains the safe default at 58% adoption in Meta's 2025 typing survey, with Rust-based checkers (Pyrefly, ty, Zuban) collectively above 20% and worth evaluating in parallel. For cross-file consistency, teams using Context Engine inside Cosmos workflows catch interface mismatches across modules that single-module type checking cannot see.

7. Exception Handling Specificity

Priority: Must-fix. Pass: No bare except clauses, specific exception types caught, logger.exception() used for tracebacks. Broad handlers mask the bugs that Python error handling tactics exist to surface.

8. Assertion Specificity in Tests

Priority: Recommended. Pass: Assertions validate exact expected values (user.email == "test@example.com", not user.id is not None), and exception messages are checked with pytest.raises(match=).

9. Test Independence

Priority: Recommended. Pass: Tests run in any order with no shared mutable state. Order-dependent tests make failures nondeterministic. This check gains weight for teams targeting Python 3.14's free-threaded build, which is now officially supported; without the GIL serializing access, shared mutable state bugs surface more often.

10. Fixture Scoping

Priority: Recommended. Pass: Expensive setup uses @pytest.fixture(scope="session"); isolation-sensitive setup uses scope="function".

Readability and Structure (Checks 11-14)

These checks require human judgment but adhere to consistent thresholds, improving maintainability without blocking deployment.

11. Cyclomatic Complexity

Priority: High. Pass: ≤10 linearly independent paths per function. Ten is a common team convention rather than a language rule; pick a number, write it into the review guide, and apply it consistently.

12. Function Length

Priority: High. Pass: Functions fit on one screen with one responsibility; most teams set the bar somewhere in the 40-60 line range. Long functions typically bundle concerns that refactoring techniques exist to separate.

13. Context Manager Usage

Priority: Must-fix. Pass: File operations, connections, and locks use with statements so cleanup runs on every exit path, including exceptions and early returns.

14. Idiomatic Python Patterns

Priority: Must-fix. Pass: No mutable default arguments (def f(x, target=[]) is a bug, not a style choice), enumerate() instead of manual counters, comprehensions for simple transformations, and try blocks scoped to only the statement that can raise the caught exception.

Context Engine traces the cross-file dependencies and call paths that isolated linters miss, giving reviewers architectural context for the whole service.

Performance (Checks 15-16)

Profile before improving performance; cProfile remains the standard library recommendation for most users, with py-spy and Scalene as sampling alternatives for running processes.

15. Algorithmic Complexity

Priority: High, context-dependent. Pass: Membership tests against large collections use set or dict (O(1)), not lists inside loops (O(n×m)). Converting allowed_ids to a set before a filter loop is the canonical fix.

16. Memory-Efficient Patterns

Priority: Recommended. Pass: Generators for datasets larger than memory; __slots__ on classes instantiated tens of thousands of times. The official data model docs say __slots__ savings "can be significant" but publish no percentage. A third-party Python 3.12 benchmark measured 28.3 MB without __slots__, compared to 9.6 MB with slots; actual savings vary with attribute count and type.

Python Security Code Review (Checks 17-23)

Security review updated its map in November 2025, when OWASP released the Top 10:2025, its 8th edition, based on analysis of 175,000+ CVE records. Two categories are new: A03:2025 Software Supply Chain Failures and A10:2025 Mishandling of Exceptional Conditions, and SSRF is folded into A01 Broken Access Control. Any checklist still citing 2021 category numbers is over a year out of date. For tooling, run Bandit 1.9.4 in pre-commit for fast AST-level checks and Semgrep 1.172.0 in CI (semgrep scan --config p/python --error) for taint tracking and cross-file analysis; the two overlap little in practice, so running both is not duplicated work. High-severity findings in checks 17-23 block merge; lower-severity findings get risk-based triage according to AI code security practice.

17. SQL Injection Prevention (A05:2025)

Priority: Must-fix. Pass: Every query is parameterized, according to the OWASP SQL Injection Prevention Cheat Sheet. Grep for SQL strings built with +, %, .format(), or f-strings; Bandit rule B608 flags these automatically, plus B610/B611 for Django extra() and RawSQL.

python
# ❌ FAIL
query = f"SELECT * FROM users WHERE username = '{username}'"
# ✅ PASS
query = "SELECT * FROM users WHERE username = %s"
cursor.execute(query, (username,))

18. OS Command Injection (A05:2025)

Priority: Must-fix. Pass: No subprocess.run, subprocess.Popen, or subprocess.call with shell=True on user-controlled input; no os.system or os.popen with concatenated input. The OWASP OS Command Injection Defense Cheat Sheet requires parameterized argument lists instead.

19. Secure Deserialization (A08:2025)

Priority: Must-fix. Pass: No pickle.load/pickle.loads on untrusted data (including pandas.read_pickle), yaml.safe_load or Loader=yaml.SafeLoader for all YAML, no eval() or exec() on user input. Bandit covers this class with B301/B403 (pickle), B506 (unsafe YAML), and the 1.9.x-added B614 for torch.load, a rule worth noting for ML teams.

20. No Hardcoded Secrets (A02:2025)

Priority: Critical. Pass: All credentials come from environment variables or a secrets manager according to the OWASP Secrets Management Cheat Sheet; .env files are gitignored. Grep for password =, api_key =, token =, and -----BEGIN in source; Bandit rules B105-B107 catch string literals and default arguments. When teams run Context Engine across the repository, hardcoded credentials surface during development rather than in a post-incident git-history audit.

python
# ✅ PASS
import os
def connect_database():
return db.connect(
host=os.getenv("DB_HOST"),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASSWORD"),
)

21. SSRF Prevention (A01:2025)

Priority: Must-fix. Pass: Any requests.get(url), urllib.request.urlopen(url), or httpx.get(url), where the URL derives from user input, validates scheme, host, and IP against an allowlist, blocking private ranges (10.x, 172.16-31.x, 192.168.x, 169.254.x) and dangerous schemes like file:// and gopher://, according to the OWASP SSRF Prevention Cheat Sheet.

22. Cryptographic Hygiene (A04:2025)

Priority: Must-fix. Pass: Password hashing uses bcrypt, argon2, or scrypt, never MD5 or SHA1; security-sensitive values use the secrets module, not random; no ssl.CERT_NONE or verify=False in HTTP clients; no DES, RC4, or ECB mode.

23. Dependency Vulnerability Scanning (A03:2025)

Priority: Must-fix for HIGH/CRITICAL CVEs. Pass: pip-audit runs in CI against the dependency manifest (uvx pip-audit --requirement requirements.txt works for uv-managed projects), and HIGH/CRITICAL findings block merge. The new A03 category carries the highest average exploit and impact scores among CVEs in OWASP's 2025 dataset, which is why supply chain moved into the top three. Flag unpinned dependencies (package>=1.0 with no lock) and any --extra-index-url pointing at an untrusted registry. Context Engine tracks which modules actually import a flagged package, so reviewers can tell a CVE in a transitive test-only dependency from one on a request path.

Open source
augmentcode/augment-swebench-agent882
Star on GitHub

Documentation and Dependency Standards (Checks 24-25)

Documentation and dependency hygiene are the cheapest checks on this list and the most expensive to skip.

24. Google-Style Docstrings

Priority: Must-fix. Pass: All public APIs document Args, Returns, and Raises. The Google Python Style Guide requires the three-double-quote format: a one-line summary, a blank line, then the details.

25. Lock Files and Dependency Justification

Priority: Must-fix. Pass: A lock file with exact versions and hashes is committed, and new dependencies carry a written justification covering necessity, maintenance status, and security history. For uv projects, uv.lock is a cross-platform lockfile that should be checked into version control and never edited by hand; enforce it in CI with uv sync --locked or uv lock --check. Development-only requirements belong in PEP 735 [dependency-groups], which build backends must exclude from distribution metadata, though tool support still varies, so [project.optional-dependencies] remains the safe fallback.

Reviewing AI-Generated Python Code

AI assistants now write a large share of the code sitting in every review queue, and that code needs its own pass/fail criteria. The 2025 DORA report puts AI adoption at 90% among respondents, yet 66% of developers cite "AI tools that are almost right, but not quite" as their top frustration, and 46% actively distrust the accuracy of AI output. AI-generated Python needs its own review criteria for two measured reasons.

First, hallucinated dependencies. A USENIX Security 2025 study of 2.23 million generated code samples found 19.7% contained at least one fabricated package name, totaling 205,474 unique names; Python's mean hallucination rate is 23.14%, higher than JavaScript's 14.73%. These names are predictable enough to weaponize as "slopsquatting": researcher Bar Lanyado registered the hallucinated package huggingface-cli on PyPI and collected 30,000+ authentic downloads in three months, including from an Alibaba repository README.

Second, security failure rates. Veracode's 2025 GenAI Code Security Report analyzed 80 coding tasks across 100+ LLMs and found AI-generated code introduced OWASP Top 10 vulnerabilities in 45% of cases. Python, C#, and JavaScript had failure rates between 38% and 45%, with Java the worst at over 70%. LLMs also failed to secure code against cross-site scripting (CWE-80) and log injection (CWE-117) in 86% and 88% of cases, respectively. Security performance stayed flat regardless of model size or release date. A Stanford study presented at ACM CCS 2023 adds the human factor: participants with access to an AI assistant wrote less secure code and were more confident it was secure.

Apply these checks to every AI-assisted PR:

  1. Verify every new package against the live PyPI registry before installation. Never trust an import or pip install line on sight.
  2. Run Bandit and Semgrep on every AI-generated diff. AI output is syntactically clean and well formatted, which is exactly why insecure code gets waved through.
  3. Weight review toward the highest-failure classes: XSS, log injection, SQL injection, deserialization, and hardcoded credentials.
  4. Require AI-assistance disclosure in commits. The Linux kernel now mandates an Assisted-by: tag, with the human submitter holding full accountability; Fedora requires a similar commit trailer.
  5. Check API currency against pinned versions. API knowledge conflicts, including deprecated interfaces and incorrect parameters, account for 20.41% of LLM code hallucinations, according to research from ACM ISSTA 2025.
  6. Assign a named human owner to every AI-generated change. DORA calls the alternative a "verification tax": time saved writing code gets re-spent auditing it.

Tooling shrinks that tax. Context Engine within Cosmos workflows reads the entire repository before commenting, so reviewers on AI-heavy PRs start from diffs already checked against real interfaces, real dependency versions, and existing call sites, rather than raw model output. Reading the whole repository is only the precondition. The separate decision for any team adopting AI for code review is how many candidate findings survive to the pull request.

Automate the Checklist and Focus Reviewers on What Machines Cannot Do

Start with the automation layer this week: commit the .pre-commit-config.yaml from check 4, add --cov-fail-under=80 and mypy --strict to CI, and wire pip-audit into the merge gate. That automates most of the style, type, coverage, and dependency checks above and leaves reviewers the work machines cannot do: architecture, domain logic, and the AI-generated diffs that now make up a large share of every review queue.

Context Engine applies these checks repository-wide through semantic dependency analysis, catching cross-file type mismatches, secrets, and injection paths that per-file reviews miss. Cosmos is generally available and included on all paid plans.

Frequently Asked Questions About Python Code Review

These are the questions engineering teams ask when rolling out a shared Python review checklist.

Written by

Molisha Shah

Molisha Shah

Molisha is an early GTM and Customer Champion at Augment Code, where she focuses on helping developers understand and adopt modern AI coding practices. She writes about clean code principles, agentic development environments, and how teams are restructuring their workflows around AI agents. She holds a degree in Business and Cognitive Science from UC Berkeley.


Get Started

Give your codebase the agents it deserves

Install Augment to get started. Works with codebases of any size, from side projects to enterprise monorepos.