System Design

Project Case Study: Designing Stripe’s Ledger System

How does Stripe ensure 100% auditability and data integrity for millions of accounts? A deep dive into Double-Entry Bookkeeping, the Outbox Pattern, and Sharding.

Sachin Sarawgi·April 20, 2026·2 min read
#system-design#ledger#fintech#scalability#acid#distributed-systems

Project Case Study: Designing Stripe’s Ledger System

Recording transactions between millions of accounts is not a matter of simple arithmetic. It requires a system that is 100% auditable, ACID-compliant, and perfectly consistent.

1. Requirements

Functional:

  • Immutable Audit Trail: Every cent moved must be recorded forever.
  • Precision: 100% accuracy (no rounding errors).
  • Global Support: Transactions spanning multiple regions.

Non-Functional:

  • Transactional Integrity (ACID): Atomic updates for all account movements.
  • High Throughput: Thousands of ledger entries per second.
  • Availability: 99.999% uptime.

2. High-Level Design (HLD)

Stripe’s ledger doesn't just store a "Balance" field. It uses Double-Entry Bookkeeping.

  • Every transaction creates two entries: a Debit and a Credit.
  • Entries table: id, account_id, amount, currency, timestamp, transaction_id.
  • The true balance of an account is always: SUM(Entries.amount) where account_id = X.

3. Low-Level Design (LLD): Transactional Integrity

We use the Transactional Outbox Pattern to ensure we don't have "Dual Write" problems between our Database and our Event Bus (Kafka).

  1. Start SQL Transaction.
  2. Write Ledger Entries: Insert the Debit/Credit rows.
  3. Write to Outbox Table: Store the event that a payment happened.
  4. Commit Transaction.
  5. CDC (Change Data Capture): A process like Debezium polls the Outbox table and pushes events to Kafka for downstream consumption.

4. Scaling Challenges: Sharding

We shard the Entries table by account_id using Consistent Hashing. This ensures that all entries for a specific account live on the same physical database node, allowing for fast, atomic updates to that account's state.

Summary

Designing a ledger is about Immutability. By treating the ledger as an append-only stream of entries and using the Outbox pattern for integration, you can build a financial system that scales to global volumes with total confidence in its accuracy.


Next: Designing a Distributed Message Queue Previous: Beyond CAP: The PACELC Theorem

📚

Recommended Resources

Designing Data-Intensive ApplicationsBest Seller

The definitive guide to building scalable, reliable distributed systems by Martin Kleppmann.

View on Amazon
Kafka: The Definitive GuideEditor's Pick

Real-time data and stream processing by Confluent engineers.

View on Amazon
Apache Kafka Series on Udemy

Hands-on Kafka course covering producers, consumers, Kafka Streams, and Connect.

View Course

Practical engineering notes

Get the next backend guide in your inbox

One useful note when a new deep dive is published: system design tradeoffs, Java production lessons, Kafka debugging, database patterns, and AI infrastructure.

No spam. Just practical notes you can use at work.

Sachin Sarawgi

Written by

Sachin Sarawgi

Engineering Manager and backend engineer with 10+ years building distributed systems across fintech, enterprise SaaS, and startups. CodeSprintPro is where I write practical guides on system design, Java, Kafka, databases, AI infrastructure, and production reliability.

Found this useful? Share it: