Skip to content

Code Quality Guide

ESLint

ESLint is a static analysis tool that finds and fixes problems in your code. It catches bugs, enforces coding standards, and improves consistency across your team.

What's Configured

This project uses:

  • eslint-config-next — Next.js recommended rules including Core Web Vitals
  • eslint-plugin-jsx-a11y — accessibility rules that catch common a11y issues in JSX

Running ESLint

bash
pnpm lint        # Check for issues
pnpm lint:fix    # Automatically fix issues where possible

What eslint-plugin-jsx-a11y Adds

This plugin catches accessibility mistakes like:

  • Missing alt text on images
  • Non-interactive elements with click handlers (should be buttons)
  • Missing aria-label on icon-only buttons
  • Form inputs without associated labels

Prettier

Prettier is an opinionated code formatter. It enforces a consistent style across all files — no more debating tabs vs spaces or where to put semicolons.

Configured Rules

RuleValueWhat It Does
semitrueAlways use semicolons
singleQuotetrueUse single quotes for strings
tabWidth22 spaces for indentation
trailingCommaes5Trailing commas where valid in ES5
printWidth100Wrap lines at 100 characters

Running Prettier

bash
pnpm format        # Format all files
pnpm format:check  # Check if files are formatted (used in CI)

Husky & lint-staged

Husky manages git hooks — scripts that run automatically at certain points in the git workflow.

lint-staged runs linters only on staged files (files you're about to commit), making pre-commit checks fast.

What Happens on Commit

When you run git commit:

  1. pre-commit hook runs lint-staged:
    • ESLint checks and fixes .ts and .tsx files
    • Prettier formats all staged files
  2. commit-msg hook runs commitlint:
    • Validates your commit message follows Conventional Commits format

If either check fails, the commit is blocked. Fix the issues and try again.

Conventional Commits

All commit messages must follow the Conventional Commits format:

type: description

All Valid Types

TypeDescriptionExample
featNew featurefeat: add search functionality
fixBug fixfix: correct date formatting
docsDocumentation changesdocs: update deployment guide
styleFormatting only (no logic change)style: fix indentation in utils
refactorCode reorganization (no feature/fix)refactor: simplify auth logic
testAdding or updating teststest: add button component tests
choreMaintenance taskschore: update dependencies
perfPerformance improvementperf: lazy load dashboard charts
ciCI/CD changesci: add preview deployment step
buildBuild system changesbuild: upgrade Tailwind to v4
revertRevert a previous commitrevert: undo auth flow changes

When a Commit Is Blocked

If your commit is rejected:

  1. Linting failure: Run pnpm lint:fix and pnpm format, then re-stage and commit
  2. Commit message failure: Rewrite your message following the format above
  3. Tip: Use git commit -m "feat: your description" to ensure the format is correct

Extending the Configuration

Adding ESLint Rules

Edit eslint.config.mjs to add or override rules:

javascript
{
  rules: {
    'no-console': 'warn', // Warn on console.log usage
  },
}

Changing Prettier Rules

Edit .prettierrc to adjust formatting preferences. See Prettier Options for all available options.

Useful Resources

Export Doc Comment Convention

All exported symbols in application code should have a JSDoc-style block comment directly above the export.

Required Structure

  • First line: A short purpose statement (what this export does)
  • @param tags: Required for function/component/hook parameters
  • @returns tag: Required for function/component/hook return behavior
  • Types and constants: Use a concise intent statement (no @param/@returns needed)
ts
/**
 * Executes the <action> operation.
 * @param input - Input payload for the operation.
 * @returns The computed result.
 */
export function example(input: string): string {
  return input;
}
ts
/**
 * Defines the shape of <domain object>.
 */
export interface Example {
  id: string;
}
ts
/**
 * Defines the <CONSTANT_NAME> constant.
 */
export const EXAMPLE_LIMIT = 10;