Software Engineering5 min read

Code Quality & Static Analysis

Enforce code quality with ESLint flat config, Prettier, TypeScript strict mode, Husky git hooks, and CI quality gates.

lintingeslintcode-qualitystatic-analysis

Code quality tooling catches bugs and style issues automatically - before they reach review. The modern stack: ESLint (flat config), Prettier (formatting), TypeScript strict mode (type safety), Husky (pre-commit hooks), lint-staged (run linters on changed files only).

Automated formatting (Prettier) eliminates style debates entirely - configure and forget. ESLint catches real bugs; tune rules to be meaningful, not noisy. TypeScript strict mode is non-negotiable for production code. Fix quality issues at the source (pre-commit) not at the review.

Workflow

  1. Configure ESLint with flat configESLint v9+ uses flat config - simpler to compose and understand than the old `extends` system.
  2. Integrate Prettier with ESLintPrettier eliminates formatting debates - one canonical style, auto-applied.
  3. Enable TypeScript strict modeStrict mode catches an entire category of bugs - null pointer errors, implicit any, unsafe operations.
  4. Set up Husky with lint-stagedPre-commit hooks catch issues before they're committed; lint-staged runs linters only on changed files.
  5. Add CI quality gatesPre-commit hooks can be bypassed; CI gates are the final enforcement layer.

Output Format

ESLint flat config with typescript-eslint strict, Prettier with prettier-plugin-tailwindcss, tsconfig `strict: true` + `noUncheckedIndexedAccess`, Husky pre-commit with lint-staged, CI jobs for lint/typecheck/test.

Examples

Flat config: `import ts from "typescript-eslint"; export default ts.config({ files: ["**/*.ts","**/*.tsx"], extends: [...ts.configs.strictTypeChecked], rules: { "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/consistent-type-imports": ["error",{prefer:"type-imports"}], "no-console": "warn" } })`
lint-staged: `"lint-staged": { "*.{ts,tsx}": ["eslint --max-warnings=0 --fix", "prettier --write"], "*.{json,css,md}": ["prettier --write"] }`
tsconfig strict: `{ "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "noImplicitOverride": true, "noFallthroughCasesInSwitch": true } }`

Edge Cases & Gotchas

  • ESLint and generated files: add `// eslint-disable-next-line` sparingly, or exclude generated files in `eslint.config.mjs` with `{ ignores: ["dist/**", "src/generated/**"] }` at the config level
  • Monorepo: share ESLint and Prettier config via an internal package (`packages/config/eslint.config.mjs`). Each app imports and extends: `import baseConfig from "@myorg/config/eslint.config.mjs"`
  • Pre-commit bypass: `git commit --no-verify` skips hooks. For environments where this is a concern, rely on CI gates as the authoritative check

When to Break These Rules

  • For very small personal projects or one-off scripts, full quality tooling adds more overhead than value. Set up linting and formatting but skip pre-commit hooks for personal/prototype work.

Reference Files

  • ESLint flat config: https://eslint.org/docs/latest/use/configure/configuration-files
  • typescript-eslint: https://typescript-eslint.io/getting-started/
  • Prettier: https://prettier.io/docs/en/install
  • Husky: https://typicode.github.io/husky/
  • lint-staged: https://github.com/lint-staged/lint-staged

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.