Lesson 30 of 54

Variables and State Management

Eliminating Magic Numbers and Strings

What does 86400 mean? What about 'pending'? You can probably figure it out with context, but you shouldn't have to.

Magic numbers and strings are literal values with no explanation. They force readers to figure out their meaning. They're scattered throughout the code. And when you need to change them, you have to find and update every occurrence.

The Problem

Javascript
if (attempts > 3) {
  throw new Error('Too many attempts');
}

setTimeout(callback, 86400000);

if (user.status === 'P') {
  // What does 'P' mean?
}

const fee = amount * 0.029;  // Where does 0.029 come from?

Each of these requires mental effort to understand.

Named Constants

Replace magic values with named constants:

Javascript
const MAX_LOGIN_ATTEMPTS = 3;
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
const STATUS_PENDING = 'P';
const STRIPE_TRANSACTION_FEE_RATE = 0.029;

if (attempts > MAX_LOGIN_ATTEMPTS) {
  throw new Error('Too many attempts');
}

setTimeout(callback, ONE_DAY_MS);

if (user.status === STATUS_PENDING) {
  // Clear now
}

const fee = amount * STRIPE_TRANSACTION_FEE_RATE;

Now:

  • The meaning is clear at the use site
  • Changes happen in one place
  • Search finds all usages

Naming Conventions

ALL_CAPS for True Constants

Javascript
const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
const DEFAULT_TIMEOUT_MS = 5000;
const API_BASE_URL = 'https://api.example.com';

Include Units

Javascript
// BAD: What unit is 60?
const CACHE_DURATION = 60;

// GOOD: Unit is clear
const CACHE_DURATION_SECONDS = 60;
const CACHE_DURATION_MS = 60 * 1000;

Express Intent

Javascript
// BAD: What does 0.85 mean?
const DISCOUNT = 0.85;

// GOOD: Clear meaning
const PREMIUM_MEMBER_DISCOUNT_MULTIPLIER = 0.85;  // Means 15% off
// Or
const PREMIUM_MEMBER_DISCOUNT_PERCENT = 15;
const price = basePrice * (1 - PREMIUM_MEMBER_DISCOUNT_PERCENT / 100);

When you have a set of related values, use enums:

TypeScript Enums

Typescript
enum OrderStatus {
  Pending = 'PENDING',
  Processing = 'PROCESSING',
  Shipped = 'SHIPPED',
  Delivered = 'DELIVERED',
  Cancelled = 'CANCELLED',
}

if (order.status === OrderStatus.Pending) {
  // Type-safe, autocomplete works
}

JavaScript Object Constants

Javascript
const OrderStatus = {
  PENDING: 'PENDING',
  PROCESSING: 'PROCESSING',
  SHIPPED: 'SHIPPED',
  DELIVERED: 'DELIVERED',
  CANCELLED: 'CANCELLED',
} as const;

Union Types (TypeScript)

Typescript
type OrderStatus = 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled';

function processOrder(order: { status: OrderStatus }) {
  // TypeScript ensures only valid statuses
}

Configuration Objects

Group related constants into configuration objects:

Javascript
const TIMEOUTS = {
  short: 1000,
  medium: 5000,
  long: 30000,
  veryLong: 120000,
};

const FILE_LIMITS = {
  maxSizeBytes: 10 * 1024 * 1024,
  allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
  maxCount: 10,
};

const RETRY_CONFIG = {
  maxAttempts: 3,
  initialDelayMs: 1000,
  backoffMultiplier: 2,
  maxDelayMs: 30000,
};

Now configuration is discoverable and grouped logically.

When Magic Values Are Acceptable

Obvious Values

Javascript
// These are fine
const half = total / 2;
const percentage = fraction * 100;
const doubled = value * 2;

2 in "double" and 100 in "percentage" are universally understood.

Array Indices

Javascript
// Fine if the structure is obvious
const [first, second] = items;
const last = items[items.length - 1];

Standard Constants

Javascript
// Well-known values
const circle = radius * 2 * Math.PI;

Test Data

Javascript
// Test values don't need to be constants
test('adds two numbers', () => {
  expect(add(2, 3)).toBe(5);
});

Finding Magic Values

Look for:

  • Numbers that aren't 0, 1, or 2
  • Strings that appear multiple times
  • Values that represent business rules
  • Any value where you have to ask "what does this mean?"

Code Review Checklist

When reviewing code, ask about each literal:

  1. Would a new developer understand this value?
  2. Could this value change?
  3. Is this value repeated elsewhere?

If any answer is "yes," extract to a constant.

The Change Problem

Magic values scattered throughout code create a change hazard:

Javascript
// Scattered throughout codebase
if (role === 'admin') { }
if (user.role === 'admin') { }
const isAdmin = role === 'admin';

If you need to change 'admin' to 'administrator', you have to find every occurrence. Miss one, and you have a bug.

Javascript
// Centralized
const ROLE_ADMIN = 'admin';
if (role === ROLE_ADMIN) { }
if (user.role === ROLE_ADMIN) { }
const isAdmin = role === ROLE_ADMIN;

Change the constant once, it's updated everywhere.

Domain Constants vs. Technical Constants

Distinguish between:

Domain Constants (Business Logic)

Javascript
const MINIMUM_VOTING_AGE = 18;
const FREE_SHIPPING_THRESHOLD_CENTS = 5000;
const MAX_ITEMS_PER_ORDER = 100;

These come from business requirements. They might need to be configurable.

Technical Constants

Javascript
const BYTES_PER_MEGABYTE = 1024 * 1024;
const SECONDS_PER_HOUR = 3600;
const HTTP_STATUS_OK = 200;

These are unlikely to change. They're definitional.

Configuration vs. Constants

Some "magic values" should actually be configuration:

Javascript
// If this varies by environment, it's configuration
const API_URL = process.env.API_URL || 'https://api.example.com';

// If this might be A/B tested, it's configuration
const ITEMS_PER_PAGE = getConfig('itemsPerPage', 20);

Configuration is loaded at runtime. Constants are defined at compile time.


Key insight: Magic values hide meaning and scatter knowledge. Replace them with named constants that explain what the value represents. Group related constants. Use enums for sets of related values. Make the code tell the reader what values mean.