Most developers treat errors differently depending on where they see them.
That's the problem.
If the code is broken, it should never reach production.
Not "we'll fix it later." Not "it's just a warning."
Never.
Together, they're your first line of defense.
But only if you take them seriously.
Even if you:
Things still slip through.
Why?
Local discipline doesn't scale.
CI enforces rules automatically.
No opinions. No exceptions.
If it fails, it doesn't ship.
At minimum:
npm run lint
npm run type-check
npm run build
If any of these fail → the PR is blocked.
Create .github/workflows/ci.yml:
name: CI
on:
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm install
- name: Run ESLint
run: npm run lint
- name: Type Check
run: npm run type-check
- name: Build
run: npm run build
In your package.json:
{
"scripts": {
"lint": "eslint . --max-warnings=0",
"type-check": "tsc --noEmit",
"build": "next build"
}
}
--max-warnings=0 → warnings = failuretsc --noEmit → pure type validationYou don't rely on:
It just runs.
Everyone follows the same rules.
No "works on my machine."
Bad code never reaches main.
The bigger the team → the more important CI becomes.
If CI fails, the code doesn't exist.
Simple.
Speed matters. But speed without guardrails slows you down later. CI is those guardrails.
Your future self (and your team) will thank you.