# Replace Magic Number with Symbolic Constant

> Give a literal number or string with special meaning a named constant, and use the name everywhere the value appears.

- kind: technique
- category: organizing-data
- aliases: Replace Magic Literal
- canonical: https://aipatterns.dev/refactoring/techniques/replace-magic-number-with-symbolic-constant
- inspired-by: https://refactoring.guru/replace-magic-number-with-symbolic-constant

## Problem

Literals such as `86400`, `0.2`, `429` or `"PENDING_REVIEW"` appear in the code with no explanation. Readers guess their meaning, and when a value changes you must find every copy, telling apart the copies of *this* `30` from unrelated `30`s.

## Solution

**Declare a constant with a name that states the value's meaning, and replace each occurrence that has that meaning with the name.**

## Before

```ts
async function fetchWithRetry(url: string): Promise<Response> {
  for (let attempt = 0; attempt < 4; attempt++) {
    const res = await fetch(url, { signal: AbortSignal.timeout(8000) });
    if (res.status !== 429 && res.status < 500) return res;
    await sleep(Math.min(250 * 2 ** attempt, 5000));
  }
  throw new Error(`gave up on ${url} after 4 attempts`);
}
```

## After

```ts
const MAX_ATTEMPTS = 4;
const REQUEST_TIMEOUT_MS = 8_000;
const BASE_BACKOFF_MS = 250;
const MAX_BACKOFF_MS = 5_000;
const HTTP_TOO_MANY_REQUESTS = 429;
const HTTP_SERVER_ERROR_MIN = 500;

async function fetchWithRetry(url: string): Promise<Response> {
  for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
    const res = await fetch(url, { signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });
    const retryable = res.status === HTTP_TOO_MANY_REQUESTS || res.status >= HTTP_SERVER_ERROR_MIN;
    if (!retryable) return res;
    await sleep(Math.min(BASE_BACKOFF_MS * 2 ** attempt, MAX_BACKOFF_MS));
  }
  throw new Error(`gave up on ${url} after ${MAX_ATTEMPTS} attempts`);
}
```

## Why

- Each number explains itself, and the retry policy reads as a policy.
- Changing the attempt count updates the loop and the error message together; the two can no longer drift.
- Constants are searchable. `MAX_ATTEMPTS` finds every use; `4` finds noise.

## Steps

1. Pick a literal and decide its meaning. Name the meaning, not the value (`MAX_ATTEMPTS`, not `FOUR`).
2. Declare the constant in the narrowest scope that covers all its uses: function, module, or a shared config module.
3. Find every occurrence of the literal. Replace only those with the same meaning. Run the tests.
4. If a set of related literals form an enumeration (statuses, roles), consider an `enum`, a union type, or [Replace Type Code with Class](https://aipatterns.dev/refactoring/techniques/replace-type-code-with-class.md) instead.

## Watch out

- Don't name obvious values: `0` as a starting index, `1` in `n + 1`, `100` in a percentage formula, `2` in "halve this".
- Two equal literals with different meanings must get two constants. Merging them couples unrelated rules.
- If a value differs between environments or tenants, it is configuration, not a constant. Load it from config.
- If the literal needs behavior (validation, formatting, units), it wants a type: [Replace Data Value with Object](https://aipatterns.dev/refactoring/techniques/replace-data-value-with-object.md).
- Inverse: inlining a constant back is rarely useful, except for a constant used once whose name repeats the value.

## Related

- Fixes [Primitive Obsession](https://aipatterns.dev/refactoring/smells/primitive-obsession.md), [Duplicate Code](https://aipatterns.dev/refactoring/smells/duplicate-code.md) and [Comments](https://aipatterns.dev/refactoring/smells/comments.md) that explain numbers.
- [Extract Variable](https://aipatterns.dev/refactoring/techniques/extract-variable.md) is the local, computed equivalent.
- [Replace Type Code with Class](https://aipatterns.dev/refactoring/techniques/replace-type-code-with-class.md) handles sets of related codes.

## Agent checklist

- [ ] Each constant name describes meaning, not value.
- [ ] Only occurrences with the same meaning were replaced.
- [ ] Constants live in the narrowest sensible scope.
- [ ] Environment-dependent values were moved to configuration instead.
- [ ] Tests pass.
