---
title: What Stripe Figured Out
slug: exemplar-stripe
kind: exemplar
summary: That money software is read far more often than it is written, so the product is built around inspecting a past event rather than performing a new one.
problem: >-
  Financial tools are usually designed around the transaction — the form that
  takes the payment. But almost all the time anyone spends in one is afterwards,
  asking what happened to a specific payment and why.
family: [inspect, scan]
principles: [progressive-disclosure, orientation]
status: stable
visibility: public
related:
  - title: Activity Timeline
    url: /patterns/activity-timeline
    summary: The pattern their payment view is built on — history first, fields second.
  - title: Dense Operational Table
    url: /patterns/dense-operational-table
    summary: Their ledger, and the columns they refused to add.
  - title: Object Identity Header
    url: /patterns/object-identity-header
    summary: Amount and status first, because those two facts resolve most visits.
---

## The insight

**A payment is a story, not a record.** People do not open a payment to read its
fields; they open it because something is unexplained — it failed, it was
disputed, the customer says they paid, the amount is wrong. All of those are
questions about *what happened over time*.

Most financial software answers with a field list. Stripe answers with a
timeline, and puts the two facts that resolve most visits — the amount and the
status — above it.

## What is worth stealing

**Lead with the answer, not the metadata.** The payment page opens with amount
and status. Not created-at, not the object id. Those exist, further down, for
the rare visit that needs them.

**The timeline is the body of the record.** Authorised, captured, refund
requested, refund succeeded — each with a time and, crucially, a *reason* when
there is one. "Payment failed — insufficient funds" answers the support question
on its own, which is the difference between a log and an explanation.

**Ruthless column discipline.** The payment object has dozens of fields; about
six reach the ledger table. Amount right-aligned in tabular figures, status as a
word with a small dot, and almost nothing else. What is absent is the design.

**Search is a matching problem, not an interface problem.** Their search takes
an amount, an email, a card fingerprint or an object id and routes each to the
right kind of record. The box is trivial; the ranking is the product.

**Almost nothing is modal.** Reading a ledger is not a bounded interruption, so
modals are reserved for confirmations and short forms — which is why the ones
that do appear carry weight.

## Where it does not transfer

Stripe's audience is technical and its records are immutable events. Both make
the timeline model easier than it will be for you: mutable records need
[version history](/patterns/version-history) as well as an activity log, and a
non-technical audience needs the vocabulary translated out of the schema.

Their density also assumes daily use. The same table shown to someone who visits
monthly needs more labelling than they provide.

## The extraction

> **When a record exists to explain something, its history is the record and
> its fields are the footnotes.**

Ask, for any record type: do people arrive here to *read the values*, or to
*understand what happened*? If it is the second, the page should open with the
sequence of events and treat the field list as reference material.
