technical shaping — schema
Schema decisions are meaning-carrying
The schema is where the system remembers things. What it remembers, and in what shape, decides what questions can be asked of it later. A schema with a charge_attempts table can answer which charges failed and why. A schema without one cannot — the question simply has no place to land. The shape of the data is the shape of the questions the system can answer.
The schema is not the same as the domain model — the conceptual entities the system reasons about (a Subscription, a Charge Attempt, a Customer, a Plan, a Cycle) and the rules that govern them. The domain model is the thinking; the schema is one way of storing the result of that thinking. Senior engineers shape both, and the chain depends on both being honest about what the brief witnessed.
┌─ subscriptions ───────────────────┐ ┌─ charge_attempts ──────────────────┐
│ id uuid PK │ │ id uuid PK │
│ customer_id uuid FK │ │ subscription_id uuid FK │
│ plan_id uuid FK │ │ idempotency_key text U │
│ status enum │ │ status enum │
│ next_billing_at timestamp │ │ failure_category text │
│ │ │ cardcom_response jsonb │
└───────────────────────────────────┘ └────────────────────────────────────┘Two columns deserve a moment. idempotency_key is unique per attempt — the same key on a retry tells Cardcom this is the same charge, not a new one, so the customer isn't billed twice. (Idempotency is the property of an operation that can be repeated without changing the outcome beyond the first time; it's foundational in distributed systems and worth knowing if you don't already.) failure_category is an enumerated reason: rate_limited, stale_plan_ref, card_expired, provider_error. The categories are not arbitrary — they are derived from observation of the seven months Uri has been reconciling. The schema records what the brief witnessed.