# ADR-021: Code Formatting and Linting Unification

**Date**: 2025-12-12

**Status**: Accepted

**Implemented**: 2026-01-09

## Context

The project contains both frontend (React) and backend (Node.js) code. While linters may be in use, there isn't a single, enforced standard for code style and quality across the entire repository. This leads to inconsistent code and time wasted in code reviews on stylistic debates.

## Decision

We will mandate the use of **Prettier** for automated code formatting and a unified **ESLint** configuration for code quality rules across both frontend and backend. This will be enforced automatically using a pre-commit hook managed by **Husky** and **lint-staged**.

## Consequences

**Positive**: Improves developer experience and team velocity by automating code consistency. Reduces time spent on stylistic code review comments. Enhances code readability and maintainability.

**Negative**: Requires an initial setup and configuration of Prettier, ESLint, and Husky. May require a one-time reformatting of the entire codebase.

## Implementation Status

### What's Implemented

- ✅ **Prettier Configuration** - `.prettierrc` with consistent settings
- ✅ **Prettier Ignore** - `.prettierignore` to exclude generated files
- ✅ **ESLint Configuration** - `eslint.config.js` with TypeScript and React support
- ✅ **ESLint + Prettier Integration** - `eslint-config-prettier` to avoid conflicts
- ✅ **Husky Pre-commit Hooks** - Automatic enforcement on commit
- ✅ **lint-staged** - Run linters only on staged files for performance

## Implementation Details

### Prettier Configuration

The project uses a consistent Prettier configuration in `.prettierrc`:

```json
{
  "semi": true,
  "trailingComma": "all",
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "endOfLine": "auto"
}
```

### ESLint Configuration

ESLint is configured with:

- TypeScript support via `typescript-eslint`
- React hooks rules via `eslint-plugin-react-hooks`
- React Refresh support for HMR
- Prettier compatibility via `eslint-config-prettier`
- **Relaxed rules for test files** (see below)

```javascript
// eslint.config.js (ESLint v9 flat config)
import globals from 'globals';
import tseslint from 'typescript-eslint';
import pluginReact from 'eslint-plugin-react';
import pluginReactHooks from 'eslint-plugin-react-hooks';
import pluginReactRefresh from 'eslint-plugin-react-refresh';
import eslintConfigPrettier from 'eslint-config-prettier';

export default tseslint.config(
  // ... configurations
  eslintConfigPrettier, // Must be last to override formatting rules
);
```

### Relaxed Linting Rules for Test Files

**Decision Date**: 2026-01-09

**Status**: Active (revisit when product nears final release)

The following ESLint rules are relaxed for test files (`*.test.ts`, `*.test.tsx`, `*.spec.ts`, `*.spec.tsx`):

| Rule                                 | Setting | Rationale                                                                                                  |
| ------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------- |
| `@typescript-eslint/no-explicit-any` | `off`   | Mocking complexity often requires `any`; strict typing in tests adds friction without proportional benefit |

**Rationale**:

1. **Tests are not production code** - The primary goal of tests is verifying behavior, not type safety of the test code itself
2. **Mocking complexity** - Mocking libraries often require type gymnastics; `any` simplifies creating partial mocks and test doubles
3. **Testing edge cases** - Sometimes tests intentionally pass invalid types to verify error handling
4. **Development velocity** - Strict typing in tests slows down test writing without proportional benefit during active development

**Future Consideration**: This decision should be revisited when the product is nearing its final stages. At that point, stricter linting in tests may be warranted to ensure long-term maintainability.

```javascript
// eslint.config.js - Test file overrides
{
  files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'],
  rules: {
    '@typescript-eslint/no-explicit-any': 'off',
  },
}
```

### Pre-commit Hook

The pre-commit hook runs lint-staged automatically:

```bash
# .husky/pre-commit
npx lint-staged
```

### lint-staged Configuration

lint-staged runs appropriate tools based on file type:

```json
{
  "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
  "*.{json,md,css,html,yml,yaml}": ["prettier --write"]
}
```

### NPM Scripts

| Script             | Description                                    |
| ------------------ | ---------------------------------------------- |
| `npm run format`   | Format all files with Prettier                 |
| `npm run lint`     | Run ESLint on all TypeScript/JavaScript files  |
| `npm run validate` | Run Prettier check + TypeScript check + ESLint |

## Key Files

| File                 | Purpose                          |
| -------------------- | -------------------------------- |
| `.prettierrc`        | Prettier configuration           |
| `.prettierignore`    | Files to exclude from formatting |
| `eslint.config.js`   | ESLint flat configuration (v9)   |
| `.husky/pre-commit`  | Pre-commit hook script           |
| `.lintstagedrc.json` | lint-staged configuration        |

## Developer Workflow

### Automatic Formatting on Commit

When you commit changes:

1. Husky intercepts the commit
2. lint-staged identifies staged files
3. ESLint fixes auto-fixable issues
4. Prettier formats the code
5. Changes are automatically staged
6. Commit proceeds if no errors

### Manual Formatting

```bash
# Format entire codebase
npm run format

# Check formatting without changes
npx prettier --check .

# Run ESLint
npm run lint

# Run all validation checks
npm run validate
```

### IDE Integration

For the best experience, configure your IDE:

**VS Code** - Install extensions:

- Prettier - Code formatter
- ESLint

Add to `.vscode/settings.json`:

```json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
}
```

## Troubleshooting

### "eslint --fix failed"

ESLint may fail on unfixable errors. Review the output and manually fix the issues.

### "prettier --write failed"

Check for syntax errors in the file that prevent parsing.

### Bypassing Hooks (Emergency)

In rare cases, you may need to bypass hooks:

```bash
git commit --no-verify -m "emergency fix"
```

Use sparingly - the CI pipeline will still catch formatting issues.
