Code Quality
This project uses a comprehensive code quality stack across both Python/Django and frontend workspaces. Python tools are configured in pyproject.toml, while frontend tools use shared workspace packages in packages/. All tools run automatically via Lefthook on pre-commit.
Quick Reference
Tool |
Scope |
Check Command |
Fix Command |
|---|---|---|---|
Ruff |
Python |
|
|
Ruff (format) |
Python |
|
|
mypy |
Python |
|
— |
djLint |
Templates |
|
|
ESLint |
Frontend |
|
|
Prettier |
Frontend |
|
|
TypeScript |
Frontend |
|
— |
Lefthook |
All |
|
— |
Python/Django Tools
Ruff
Ruff is an extremely fast Python linter and formatter written in Rust. It replaces flake8, pylint, black, isort, pyupgrade, and many other tools in a single binary.
Ruff provides two commands:
ruff check— linter (finds errors, style issues, bugs)ruff format— formatter (consistent code style)
Configuration is in pyproject.toml under [tool.ruff].
To check without modifying files:
$ ruff format --diff .
$ ruff check .
To auto-fix issues:
$ ruff format .
$ ruff check --fix .
Note
Commit your changes before running --fix so you can revert if needed. The --unsafe-fixes flag can make breaking changes — use with caution.
Enabled rule categories include:
F(Pyflakes) — undefined names, unused importsE,W(pycodestyle) — PEP 8 styleI(isort) — import sortingN(pep8-naming) — naming conventionsUP(pyupgrade) — modern Python syntaxDJ(flake8-django) — Django best practicesB(bugbear) — likely bugs and design problemsS(bandit) — security issuesC4(comprehensions) — list/dict comprehension styleSIM(simplify) — code simplificationRUF— Ruff-specific rules
For the full list of enabled rules, see pyproject.toml. Rule documentation: https://docs.astral.sh/ruff/rules/
mypy
mypy provides static type checking for Python code with Django and DRF plugin support.
Configuration is in pyproject.toml under [tool.mypy].
Key settings:
Python 3.13 target
check_untyped_defs = true— checks functions without annotationswarn_unused_ignores = true— flags unnecessary# type: ignorePlugins:
mypy_django_plugin(always),mypy_drf_plugin(if DRF enabled)Migrations are ignored via
[[tool.mypy.overrides]]
To run type checking:
$ mypy .
Or via Docker:
$ docker compose -f docker-compose.local.yml run --rm django mypy .
Note
The django_settings_module in [tool.django-stubs] must point to a valid settings file for the Django plugin to work correctly.
djLint
djLint lints and formats Django/Jinja2 templates for consistent HTML structure.
Configuration is in pyproject.toml under [tool.djlint].
Key settings:
profile = "django"— Django template syntaxindent = 2— 2-space indentationformat_css = true,format_js = true— formats embedded CSS/JSmax_line_length = 119
To lint templates:
$ djlint {project_slug}/templates/
To auto-format templates:
$ djlint --reformat {project_slug}/templates/
Ignored rules:
H006— img tag without alt (handled by accessibility testing)H030,H031— meta tag formattingT002— known djLint bug (see https://github.com/djlint/djLint/issues/687)
Frontend Tools
ESLint
ESLint v9 with flat config format provides JavaScript/TypeScript linting.
Shared config package: packages/eslint-config/
Four exported configurations:
./base.js— Foundation with TypeScript + Turbo plugin./react-internal.js— For shared React libraries (packages/ui)./vite.js— For Vite + React applications./next-js.js— For Next.js applications
Key plugins:
typescript-eslint— TypeScript supporteslint-plugin-reactandeslint-plugin-react-hooks— React ruleseslint-plugin-react-refresh— Vite HMR supporteslint-plugin-turbo— Turborepo-specific ruleseslint-plugin-only-warn— Converts errors to warnings for DXeslint-config-prettier— Disables formatting rules (Prettier handles those)
To lint all workspaces:
$ pnpm turbo lint
To auto-fix:
$ pnpm turbo lint -- --fix
Example usage in an app’s eslint.config.js:
import { viteConfig } from "@workspace/eslint-config/vite"
export default viteConfig
Prettier
Prettier provides consistent code formatting across all frontend code.
Shared config package: packages/prettier-config/
Key settings (from index.mjs):
tabWidth: 2,useTabs: falsesemi: true,singleQuote: trueprintWidth: 100endOfLine: 'lf'
Plugins:
@trivago/prettier-plugin-sort-imports— Automatic import orderingprettier-plugin-tailwindcss— Sorts Tailwind CSS classes
Import order (enforced automatically):
reactThird-party modules
@workspace/*(workspace packages)@/*(app-specific aliases)Relative imports (
./,../)
To format all files:
$ pnpm format
To check without modifying:
$ pnpm format:check
Example usage in an app’s package.json:
{
"prettier": "@workspace/prettier-config"
}
TypeScript
TypeScript provides static type checking for frontend code with strict mode enabled.
Shared config package: packages/typescript-config/
Four exported configurations:
base.json— Foundation with strict settingsreact-library.json— For shared React packagesvite-app.json— For Vite applicationsvite-node.json— For Node.js tooling (Vite config, etc.)
Key settings from base.json:
strict: true— Enables all strict type checkingnoUncheckedIndexedAccess: true— Safer array/object accesstarget: ES2022,lib: ["es2022", "DOM", "DOM.Iterable"]skipLibCheck: true— Faster builds
To type check all workspaces:
$ pnpm turbo typecheck
Example usage in an app’s tsconfig.json:
{
"extends": "@workspace/typescript-config/vite-app.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"]
}
Git Hooks with Lefthook
Lefthook is a fast Git hooks manager that orchestrates all code quality tools. It replaces Husky + lint-staged with better performance and simpler configuration.
Configuration: lefthook.yml
How Lefthook Works
parallel: true— All commands run simultaneously for speedglob— File patterns to match (e.g.,*.py,*.{ts,tsx})exclude— Paths to skip (e.g.,/migrations/)stage_fixed: true— Auto-stages files after fixing
Only staged files are processed, not the entire codebase.
What Runs on Pre-Commit
File Validation:
trailing-whitespace— Removes trailing whitespaceend-of-file-fixer— Ensures files end with newlinecheck-json,check-toml,check-yaml,check-xml— Validates syntaxdebug-statements— Prevents committing pdb/breakpointdetect-private-key— Security check for leaked keys
Python:
django-upgrade— Modernizes Django code to 5.0+ruff-check— Lints and auto-fixes Pythonruff-format— Formats Python codemypy— Static type checking
Django Templates:
djlint-reformat— Formats HTML templatesdjlint-lint— Lints templates
Frontend:
turbo-typecheck— TypeScript checking via Turborepoturbo-lint— ESLint via Turborepoprettier— Formats JS/TS/CSS/JSON files
Manual Execution
Run all hooks manually:
$ lefthook run pre-commit
Skip hooks temporarily (not recommended):
$ git commit --no-verify
IDE Integration
Recommended VS Code extensions:
Python:
ms-python.python,charliermarsh.ruffDjango:
batisteo.vscode-djangoFrontend:
esbenp.prettier-vscode,dbaeumer.vscode-eslint
Note
Use workspace settings (.vscode/settings.json) over global settings to ensure consistent behavior across the team.
Customization
Python (pyproject.toml):
Add Ruff rules: modify
[tool.ruff.lint.select]Ignore rules: add to
[tool.ruff.lint.ignore]or use# noqa: RULEinlineAdd mypy overrides: new
[[tool.mypy.overrides]]sections
Frontend (packages/*-config/):
ESLint: Modify files in
packages/eslint-config/Prettier: Modify
packages/prettier-config/index.mjsTypeScript: Modify files in
packages/typescript-config/App-specific overrides: Extend the base config in your app’s config file
Lefthook (lefthook.yml):
Add/remove hooks as needed
Disable a hook temporarily: add
skip: trueto the command