Prerequisites
Before you begin, make sure you have the following ready:
- A computer running macOS, Linux, or Windows
- Terminal or command-line access
- Administrator or sudo privileges (for system-level changes)
- A stable internet connection for downloading tools
Step 1: Code Standards in Distributed Teams
Distributed teams produce inconsistent code. One engineer uses 2-space indentation, another 4-space. One prefers functional style, another object-oriented. Without enforcement, code reviews become style arguments instead of substance discussions.
Effective code standards are:
- Automated: No human discussion required. Linters and formatters run locally and in CI.
- Non-negotiable: Standards are in the repository, not in people’s heads. New developers adopt them immediately.
- Cheap: Setup once, runs free on every commit.
- Transparent: Everyone sees what the standards are and why they exist.
This guide covers the toolchain for distributed teams: pre-commit hooks, linters, formatters, EditorConfig, CI enforcement, and review guidelines.
Step 2: Architecture: Local Enforcement First
The optimal flow:
- Engineer makes a code change
- Pre-commit hook runs linter, formatter, security check
- If checks fail, code doesn’t commit (engineer fixes locally)
- Code commits locally with confidence
- Push to remote triggers CI (which re-runs checks as final gate)
- Code review focuses on logic, not style
This means most standard violations are caught locally, never pushed. CI is a safety net, not the primary enforcement.
Step 3: EditorConfig: The First Layer
EditorConfig is a simple format that tells editors how to format code. Create a .editorconfig file in your repo root:
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{js,ts,jsx,tsx}]
indent_style = space
indent_size = 2
[*.{py,pyi}]
indent_style = space
indent_size = 4
[*.{go}]
indent_style = tab
[*.md]
trim_trailing_whitespace = false
This says: “All files use UTF-8, Unix line endings, final newline. JS files use 2-space indentation. Python uses 4-space. Go uses tabs. Markdown preserves trailing whitespace.”
EditorConfig works in: VS Code (with extension), JetBrains IDEs (native), Sublime, Vim, Emacs, Notepad++.
When an engineer opens a file, their editor automatically applies these settings. No configuration needed. Indentation just works.
Setup cost: 5 minutes. Benefit: 90% of formatting differences prevented before linting.
Step 4: Prettier: Opinionated Code Formatter
Prettier is the JavaScript/TypeScript formatter. It has no configuration (by design): you use Prettier’s opinions.
Install:
npm install --save-dev prettier
Format all files:
npx prettier --write .
Add to package.json:
{
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check ."
}
}
Prettier is opinionated: 2-space indents, semicolons, double quotes, trailing commas in multiline objects. These choices are non-negotiable. No .prettierrc config needed (though you can override if truly necessary).
The power of Prettier: zero arguments about formatting style. You run prettier --write once per sprint. All code is consistently formatted. Code reviews never mention indentation again.
For distributed teams, this is critical. A Berlin engineer and a San Francisco engineer both run prettier --write before committing. Their code formats identically. No review feedback about spacing or bracket placement.
Step 5: ESLint: JavaScript Linting
ESLint catches errors: unused variables, unreachable code, type confusion, security issues.
Install:
npm install --save-dev eslint
npx eslint --init
Create .eslintrc.json:
{
"env": {
"node": true,
"es2022": true
},
"extends": [
"eslint:recommended",
"next/core-web-vitals"
],
"rules": {
"no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"no-console": ["warn", { "allow": ["warn", "error"] }],
"eqeqeq": ["error", "always"],
"prefer-const": "error",
"no-var": "error"
}
}
Rules explained:
no-unused-vars: error unless variable starts with_(convention for intentionally unused)no-console: warn on console.log (allowed: warn, error)eqeqeq: always use===, never==prefer-const: useconstby default,letonly if reassignedno-var: ES6 scoping rules, no legacyvar
Run linting:
npx eslint . --fix
The --fix flag auto-fixes many issues (unused vars removed, == changed to ===). Anything it can’t fix requires manual correction.
Step 6: Pylint / Flake8: Python Linting
For Python, use Flake8 (syntax/logic errors) + Black (formatter, Python’s Prettier).
Install:
pip install flake8 black
Black format all files:
black .
Flake8 checks:
flake8 .
Create setup.cfg:
[flake8]
max-line-length = 88
extend-ignore = E203, W503
Black enforces 88-character line length, 4-space indents, double quotes. Flake8 checks for unused imports, undefined variables, complexity.
Combined: Black formats, Flake8 catches errors.
Step 7: Pre-Commit Hooks: Local Enforcement
Pre-commit hooks run before git commit. If they fail, the commit is blocked.
Install pre-commit:
pip install pre-commit
Create .pre-commit-config.yaml:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
args: ['--maxkb=500']
- id: check-merge-conflict
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.0
hooks:
- id: prettier
types_or: [javascript, typescript, jsx, tsx, json, yaml]
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.42.0
hooks:
- id: eslint
types: [javascript, jsx]
additional_dependencies: ['eslint']
Install the hooks:
pre-commit install
From now on, every git commit runs:
- Trailing whitespace removal
- Fix EOF
- YAML syntax check
- Reject files >500KB
- Check for merge conflict markers
- Black formatting (Python)
- Flake8 checks (Python)
- Prettier formatting (JS/TS/JSON/YAML)
- ESLint checks (JS/TS)
If any check fails, the commit is rejected. Engineer fixes the issue, stages the changes, commits again.
Example: Engineer commits with trailing whitespace:
$ git commit -m "Add user authentication"
Trim trailing whitespace.....................................................
This hook suggests to exclude these patterns from the list of files that need
to be transformed. Edit `.pre-commit-config.yaml` to add patterns:
file1.py
FAILED - hook id: trailing-whitespace
The hook fixed the trailing whitespace, but the commit failed. Engineer stages the fixed file and commits again:
$ git add file1.py
$ git commit -m "Add user authentication"
Trim trailing whitespace.....................................................
Check Yaml...................................................................
Black code formatter...........................................................
Flake8.......................................................................
All checks passed. [develop abc1234] Add user authentication
All checks pass, commit succeeds.
For distributed teams, this is essential: no matter your timezone or editor, code meets standards before pushing.
Step 8: CI: Final Enforcement Gate
Pre-commit hooks run locally. But if an engineer skips hooks (git commit –no-verify), code reaches remote without checks.
CI re-runs all checks on every push:
GitHub Actions .github/workflows/ci.yml:
name: CI
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11']
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install --upgrade pip
pip install black flake8 pylint
- name: Run Black
run: black --check .
- name: Run Flake8
run: flake8 .
- name: Run Pylint
run: pylint $(find . -name "*.py" | grep -v venv)
js-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run Prettier
run: npm run format:check
- name: Run ESLint
run: npm run lint
- name: Run Tests
run: npm test
When a PR is pushed, GitHub Actions automatically:
- Checks out code
- Runs Black format check
- Runs Flake8 lint
- Runs Pylint
- Runs Prettier check
- Runs ESLint
- Runs tests
If any check fails, the PR is marked as failing. The PR cannot be merged until all checks pass.
This is the final gate. Even if someone skips pre-commit hooks locally, CI catches the violations.
Step 9: Distributed Team Gotchas
Timezone: When Code Is Reviewed
CI runs 24/7, but humans don’t. If a Berlin engineer pushes code at 6pm, a San Francisco engineer reviews at 8am next day.
By that time, the Berlin engineer is asleep. They can’t fix linting issues immediately. Code sits in review limbo.
Solution: Run linting locally before pushing. Pre-commit hooks enforce this. By the time code reaches review, formatting is correct. Reviews focus on logic.
Onboarding: New Developer Environment
A new developer clones the repo. They run a test and fail unexpectedly.
Why? Their environment is different (Node version 16 vs 18, Python 3.9 vs 3.11).
Solution: Include setup documentation:
### Step 10: Development Setup
1. Clone repo: `git clone ...`
2. Install dependencies: `npm install` or `pip install -r requirements.txt`
3. Install pre-commit hooks: `pre-commit install`
4. Run tests: `npm test` or `pytest`
5. Make a test commit to verify linting works
All team members use identical tools: Node LTS, Python 3.11, pre-commit v3.3.
Include a Makefile or justfile with common commands:
.PHONY: setup lint format test
setup:
pip install -r requirements.txt
npm install
pre-commit install
lint:
flake8 .
npm run lint
format:
black .
prettier --write .
test:
pytest
npm test
New developers run make setup once. Everything is configured.
Exceptions: When Rules Don’t Apply
Sometimes a rule is wrong for a specific file. Example: a test file uses assert (normally flagged by some linters as bad practice).
Add rule exceptions:
Python (in the file):
# flake8: noqa
# This file tests internal implementation, needs lower-level assertions
import internal_module
JavaScript (in the file):
// eslint-disable-next-line no-console
console.log('Debug output for testing');
But exceptions are red flags in code review. If you need to disable a rule, question why. Is the rule wrong? Should the code be refactored? Are you using the right file for this case?
Distribute exceptions sparingly.
Step 11: Code Review Guidelines
Code review should not mention style. Pre-commit + CI handle all style.
Code review focuses on:
- Logic: Does the code solve the problem correctly?
- Edge cases: What happens with null, empty, invalid input?
- Performance: Will this scale? Any obvious bottlenecks?
- Security: Any vulnerabilities? SQL injection, XSS, auth bypass?
- Readability: Variable names clear? Comments explain non-obvious logic?
- Tests: Are new features tested? Do tests cover edge cases?
This is what humans should discuss. Style is for machines.
Step 12: Remote Team Success Metrics
After 2 weeks of enforcement:
- Commit success rate: Percentage of commits that pass linting on first try. Target: >95%.
- CI failures: Percentage of PRs that fail CI. Target: <5%.
- Review style comments: Number of code review comments about indentation, naming, formatting. Target: 0.
- Developer satisfaction: Is linting helping or annoying? Measure with surveys. Target: >4/5.
- Onboarding time: Time for new developer to make first approved PR. Target: <1 week.
If CI passes at 95% and code reviews have zero style comments, you’ve achieved enforcement.
Step 13: Recommendation Matrix
Use EditorConfig + Prettier + ESLint for JavaScript teams:
- Covers formatting, style, and error detection
- Fast feedback (pre-commit)
- Minimal configuration
- Works across editors and OS
Use EditorConfig + Black + Flake8 for Python teams:
- Black is Python’s Prettier
- Flake8 catches errors
- Same structure as JS teams
Use pre-commit hooks for all teams:
- Zero friction enforcement
- Catches issues before pushing
- Works offline
- CI is the safety net
Invest in onboarding documentation:
- New developers must understand the toolchain
- Include
make setupcommand - Document exceptions and when they apply
Troubleshooting
Configuration changes not taking effect
Restart the relevant service or application after making changes. Some settings require a full system reboot. Verify the configuration file path is correct and the syntax is valid.
Permission denied errors
Run the command with sudo for system-level operations, or check that your user account has the necessary permissions. On macOS, you may need to grant terminal access in System Settings > Privacy & Security.
Connection or network-related failures
Check your internet connection and firewall settings. If using a VPN, try disconnecting temporarily to isolate the issue. Verify that the target server or service is accessible from your network.
Frequently Asked Questions
How long does it take to set up remote team code standards enforcement (2026)?
For a straightforward setup, expect 30 minutes to 2 hours depending on your familiarity with the tools involved. Complex configurations with custom requirements may take longer. Having your credentials and environment ready before starting saves significant time.
What are the most common mistakes to avoid?
The most frequent issues are skipping prerequisite steps, using outdated package versions, and not reading error messages carefully. Follow the steps in order, verify each one works before moving on, and check the official documentation if something behaves unexpectedly.
Do I need prior experience to follow this guide?
Basic familiarity with the relevant tools and command line is helpful but not strictly required. Each step is explained with context. If you get stuck, the official documentation for each tool covers fundamentals that may fill in knowledge gaps.
Can I adapt this for a different tech stack?
Yes, the underlying concepts transfer to other stacks, though the specific implementation details will differ. Look for equivalent libraries and patterns in your target stack. The architecture and workflow design remain similar even when the syntax changes.
Where can I get help if I run into issues?
Start with the official documentation for each tool mentioned. Stack Overflow and GitHub Issues are good next steps for specific error messages. Community forums and Discord servers for the relevant tools often have active members who can help with setup problems.