---
title: "Model each state as its own type · Agent Wranglers"
url: https://agentwranglers.se/playbook/architecture/data-shapes/states-as-types
updated: 2026-10-20
licence: "All rights reserved"
---

# Model each state as its own type

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Recommended
- Chapter: [Data shapes](https://agentwranglers.se/playbook/architecture/data-shapes)
- Rule: When an object moves through states, give each state its own type with only the fields it has. Never pair a status field with optional fields that some states leave empty.
- How: List the states, such as pending, paid and refunded. Give each one a type with its own required fields, such as `paidAt` only on a paid order. Join them in a tagged union, a sealed class or an enum with data. Handle every case with an exhaustive `switch` or `match`.
- Check: The type checker rejects reading `paidAt` on a pending order. An exhaustiveness check fails when a state is not handled. Examples are typescript-eslint `switch-exhaustiveness-check`, `assert_never` with mypy, and `when` on a Kotlin sealed class.

Example:

```ts
// Bad: which dates are set when the status is 'refunded'?
type Order = { status: 'pending' | 'paid' | 'refunded'; paidAt?: Date; refundedAt?: Date };

// Good: each state carries exactly its own fields
type Order =
  | { status: 'pending' }
  | { status: 'paid'; paidAt: Date }
  | { status: 'refunded'; paidAt: Date; refundedAt: Date };
```

```python
@dataclass(frozen=True)
class Pending: ...

@dataclass(frozen=True)
class Paid:
    paid_at: datetime

Order = Pending | Paid

def describe(order: Order) -> str:
    match order:
        case Pending():
            return "Waiting for payment"
        case Paid(paid_at=paid_at):
            return f"Paid on {paid_at:%Y-%m-%d}"
        case _:
            assert_never(order)
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers architecture playbook: https://agentwranglers.se/playbook/architecture/data-shapes/states-as-types`.

- Previous: [Make data objects immutable](https://agentwranglers.se/playbook/architecture/data-shapes/immutable-data)
- Next: [Use enums instead of magic strings](https://agentwranglers.se/playbook/architecture/data-shapes/enums-not-strings)
- [Data shapes](https://agentwranglers.se/playbook/architecture/data-shapes)
