Enforcing Code Standards Across a Multi-Language Team
Code quality in a team isn't about personal preference, it's about reducing review friction and preventing entire classes of bugs. This is the linting and formatting setup we use across TypeScript and Python in a shared monorepo, enforced automatically in CI.
Code quality at scale requires automation. This guide covers our complete linting and formatting strategy across a polyglot monorepo: ESLint with Turborepo caching, Prettier for formatting, Python tools (Black, Flake8, Isort), and CI/CD enforcement.
Part 1: Scalable Linting with Turborepo & ESLint
Shared Configuration
Instead of copy-pasting .eslintrc.js files, we publish a local @repo/eslint-config package.
- packages/eslint-config/nest.js: Base rules for all NestJS microservices.
- apps/api/.eslintrc.js: Extends the base config.
module.exports = {
extends: ["@repo/eslint-config/nest.js"]
};
Intelligent Caching
Turborepo knows your dependency graph. If you only change the payment service, turbo lint will only re-lint that service, saving minutes on every build.
{
"pipeline": {
"lint": {
"dependsOn": ["^lint"],
"outputs": []
}
}
}
Parallel Execution
Running npm run lint executes all lint tasks in parallel. Caching ensures that unchanged services hit the cache instantly.
> turbo run lint
api:lint: cache hit, replaying output...
payment:lint: cache hit, replaying output...
client:lint: cache miss, executing...
Part 2: Opinionated Formatting with Prettier
The One Source of Truth
Our format rules are encoded in .prettierrc, respected by IDEs, commit hooks, and CI:
{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100
}
ESLint Integration
Using eslint-config-prettier is mandatory to disable conflicting rules:
extends: [
'plugin:prettier/recommended',
]
Beyond JavaScript
We use Prettier for everything:
- JSON:
tsconfig.json,package.json - YAML:
docker-compose.yml, GitHub Actions - Markdown:
README.md, Documentation
Part 3: Strict Python Code Quality
Black, Flake8, Isort
Three non-negotiable tools for Python:
- Black: "The Uncompromising Code Formatter". No arguments about spacing.
- Flake8: Catches logic errors and unused imports.
- Isort: Sorts your imports alphabetically and by type.
Centralized Config
[tool.black]
line-length = 88
target-version = ['py311']
[tool.isort]
profile = "black"
line_length = 88
Pre-Commit Integration
We use pre-commit hooks to fix issues before they even reach the repo.
# .pre-commit-config.yaml
- repo: https://github.com/psf/black
rev: 23.3.0
hooks: [id: black]
Part 4: Automating Quality Checks in CI/CD
The Quality Workflow
Local hooks can be bypassed. The CI pipeline is the final arbiter of quality. We run this on every Pull Request.
GitHub Actions Workflow
name: Quality Check
on:
pull_request:
branches: [main]
jobs:
lint-node:
name: Lint Node.js (ESLint + Prettier)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'
- run: npm ci
- run: npx turbo run lint
lint-python:
name: Lint Python (Black + Flake8)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- run: pip install black flake8
- run: black --check .
- run: flake8 .
Production Tips
- Branch Protection: Configure GitHub Settings to require the
Lint Node.jsandLint Pythonchecks to pass before merging. This prevents "broken windows" in your codebase. - Husky: Use Husky to run linters on
pre-commitlocally. It saves CI minutes by catching errors before the push. - Rule Depreciation: Review your ESLint rules every 6 months. If a rule is constantly ignored or disabled, remove it. Rules should help, not hinder.
Working on something similar?
I'm a Technical Lead & AI Engineer building LLM-powered SaaS in production.
NestJS, Next.js and Django on Azure, from model integration to the architecture around it. If your team is working through the same problems, I'm happy to compare notes.
Get in touchWritten by

Technical Lead at iAgency (Casablanca), previously Technical Lead leading a 5-engineer team at Fygurs on Azure cloud-native SaaS. Graduate of 1337 Coding School (42 Network / UM6P). Writes about architecture, cloud infrastructure, and engineering leadership.