# Hyrum's Law in API Design: Managing Accidental Dependencies and Observable Behaviors

> Understand Hyrum's Law in API design. Learn how to manage accidental dependencies and observable behaviors to build more robust and maintainable APIs.

- Repository: [Addy Osmani/agent-skills](https://github.com/addyosmani/agent-skills)
- Tags: deep-dive
- Published: 2026-04-16

---

**With a sufficient number of users of an API, all observable behaviors of your system will be depended on by somebody, regardless of what you promise in the contract.**

Hyrum's Law represents a critical principle in software engineering that challenges how we think about API contracts and backward compatibility. As documented in the `addyosmani/agent-skills` repository's API and Interface Design skill, this law explains why seemingly minor implementation details become de-facto public contracts once enough users depend on them.

## What Is Hyrum's Law?

Hyrum's Law states that *"with a sufficient number of users of an API, all observable behaviors of your system will be depended on by somebody, regardless of what you promise in the contract."* This principle originates from software engineer Hyrum Wright and is captured in the repository at [`/skills/api-and-interface-design/SKILL.md`](https://github.com/addyosmani/agent-skills/blob/main//skills/api-and-interface-design/SKILL.md).

Unlike formal API contracts that define explicit guarantees, Hyrum's Law recognizes that **any observable behavior**—including error message text, response ordering, timing characteristics, and undocumented quirks—will eventually be treated as a reliable dependency by some subset of users.

## Why Hyrum's Law Matters for API Design

Understanding Hyrum's Law is essential for building maintainable interfaces because it fundamentally changes how we approach stability and change management.

### Every Observable Detail Becomes a Contract

Once users discover a behavior—whether documented or not—they build systems around it. Error message strings that help with debugging today become regex patterns that parse production logs tomorrow. The [`/skills/api-and-interface-design/SKILL.md`](https://github.com/addyosmani/agent-skills/blob/main//skills/api-and-interface-design/SKILL.md) file emphasizes that **implementation leaks are dangerous** because any observable behavior can become load-bearing infrastructure for downstream consumers.

### Silent Changes Become Breaking Changes

When you modify seemingly internal details—such as changing the order of keys in a JSON response or adjusting the wording of an error message—you risk breaking user systems that implicitly depended on those specific behaviors. The repository warns that **tests alone are insufficient** because contract tests verify expected outcomes but cannot guarantee that hidden behaviors won't affect real users in production environments.

### Deprecation Requires Upfront Planning

Because any observable behavior may have dependents, removing or altering functionality requires a structured migration path. The [`/skills/deprecation-and-migration/SKILL.md`](https://github.com/addyosmani/agent-skills/blob/main//skills/deprecation-and-migration/SKILL.md) file provides guidance on safely removing observable behavior, emphasizing that **deprecation must be planned up-front** rather than treated as an afterthought.

## Design Implications of Hyrum's Law

Applying Hyrum's Law to interface design requires intentional constraints on what your system exposes and how it evolves.

### Minimize Observable Surface Area

**Be intentional about what you expose.** Limit public behavior to the minimal, well-defined surface necessary for users to accomplish their goals. Every additional observable behavior increases the maintenance burden and constrains future evolution.

### Encapsulate Implementation Details

**Avoid leaking internal implementation details.** Use abstraction layers to hide how functionality is achieved, exposing only the "what" (the contract) while concealing the "how" (the implementation). This prevents users from depending on incidental behaviors that may change.

### Treat Public Behavior as Immutable Commitments

**Treat every public behavior as a commitment.** Even seemingly innocuous changes—such as altering error-message phrasing or changing the order of array elements—can break consuming systems. Before releasing functionality, consider whether you are willing to maintain that exact behavior indefinitely.

### Design for Evolution from Day One

**Plan for deprecation at design time.** Build in versioning mechanisms, feature flags, or graceful fallbacks from the beginning so that you can retire old behavior without disrupting users. This proactive approach prevents the accumulation of technical debt that makes future changes impossible.

## Practical Implementation Strategies

The `addyosmani/agent-skills` repository provides concrete TypeScript examples demonstrating how to implement these principles in practice.

### Define Stable Contracts First

Establish explicit interfaces that represent your public commitment, separating the contract from implementation details:

```typescript
// Public contract – the only thing external callers see
export interface TaskAPI {
  /** Create a new task */
  createTask(input: CreateTaskInput): Promise<Task>;

  /** List tasks with optional filters */
  listTasks(params: ListTasksParams): Promise<PaginatedResult<Task>>;

  /** Retrieve a single task */
  getTask(id: string): Promise<Task>;

  /** Update a task – only supplied fields change */
  updateTask(id: string, input: UpdateTaskInput): Promise<Task>;

  /** Delete a task – idempotent */
  deleteTask(id: string): Promise<void>;
}

```

### Extend Fields Safely with Optionality

When adding new capabilities, preserve backward compatibility by making additions optional:

```typescript
// Safe additive change – existing consumers continue to work
export interface CreateTaskInput {
  title: string;
  description?: string;
  // NEW optional field – safe under Hyrum's Law
  priority?: 'low' | 'medium' | 'high';
  labels?: string[];
}

```

### Implement Structured Deprecation Patterns

Create explicit migration paths when retiring old behavior, maintaining both versions during a transition window:

```typescript
// New endpoint version, keeping the old one alive for a migration window
app.get('/v2/api/tasks/:id', async (req, res) => {
  // New implementation
  const task = await taskService.getTaskV2(req.params.id);
  res.json(task);
});

// Old endpoint returns a deprecation warning header
app.get('/api/tasks/:id', async (req, res) => {
  const task = await taskService.getTaskV1(req.params.id);
  res.set('Warning', '199 - "Deprecated API, migrate to /v2/api/tasks/:id"');
  res.json(task);
});

```

### Lock Down Observable Error Behaviors

Standardize error responses to prevent accidental dependencies on variable message text:

```typescript
// Centralized error serializer ensures consistent shape & wording
function serializeError(err: AppError): APIError {
  return {
    error: {
      code: err.code,
      // Fixed message to avoid accidental wording changes
      message: err.defaultMessage,
      details: err.details,
    },
  };
}

```

## Summary

- **Hyrum's Law** states that with sufficient users, all observable API behaviors become dependencies, regardless of documentation.
- **Every observable detail**—including error messages, response ordering, and timing—can become load-bearing infrastructure for downstream consumers.
- **Minimize surface area** by exposing only intentional, well-defined contracts and encapsulating implementation details.
- **Plan for deprecation** from day one using versioning, feature flags, and structured migration paths to safely evolve systems.
- **Standardize observable behaviors** like error messages to prevent accidental dependencies on variable text or structure.

## Frequently Asked Questions

### What is Hyrum's Law in software engineering?

Hyrum's Law is a principle stating that with a sufficient number of users of an API, all observable behaviors of your system will be depended on by somebody, regardless of what you promise in the contract. This means that any behavior consumers can observe—documented or not—may become a dependency that constrains future changes.

### How does Hyrum's Law affect API versioning strategies?

Hyrum's Law necessitates careful versioning strategies because even minor implementation changes can break downstream consumers who depend on observable behaviors. According to the `addyosmani/agent-skills` repository's **Deprecation and Migration** skill, APIs must plan for deprecation up-front by maintaining parallel versions during migration windows and using explicit deprecation headers to warn consumers of impending changes.

### What are practical ways to minimize observable API surface area?

To minimize observable surface area under Hyrum's Law, teams should define strict public contracts using interfaces that expose only necessary functionality, make all additive changes optional to preserve backward compatibility, and encapsulate implementation details behind stable abstractions. The repository's **API and Interface Design** skill emphasizes centralizing error serialization and standardizing response formats to prevent accidental dependencies on variable text or structure.

### Why are tests insufficient protection against Hyrum's Law?

Tests are insufficient because contract tests verify expected outcomes against documented contracts but cannot detect dependencies on hidden or incidental behaviors that real users may exploit in production. As noted in the source analysis, users may rely on error message text, response ordering, timing characteristics, or undocumented quirks that test suites do not explicitly validate, making silent changes potentially breaking for consumers despite passing all tests.