System Design
What Are ACID Transactions?
ACID is the set of guarantees that lets you trust a database with money. Four letters, four promises - and understanding exactly what each one means is foundational for system design. Here's each, with a concrete example of what breaks without it.
When you transfer money between two bank accounts, a lot has to go right. The money has to leave one account and arrive in the other - never one without the other. If the system crashes mid-transfer, you can't end up with money vanished or duplicated. If two transfers happen at once, they can't corrupt each other. And once the transfer is confirmed, it has to stay confirmed even if the power dies a second later. The set of guarantees that makes all of this trustworthy is called ACID - four properties that let you hand a database your most critical data and trust it completely.
ACID is foundational, and understanding precisely what each letter promises (and what breaks without it) is essential for reasoning about databases in system design. Let's take them one at a time, grounded in that money-transfer example.
First: what is a transaction?
ACID describes the properties of a transaction - a unit of work that groups one or more operations together so they behave as a single, indivisible action. The money transfer is a transaction: it bundles "subtract from account A" and "add to account B" into one unit that should happen completely or not at all. A transaction is how you tell the database "treat these operations as one - don't let them be half-done."
ACID is the set of guarantees the database makes about how transactions behave. The letters stand for Atomicity, Consistency, Isolation, and Durability.
stateDiagram-v2
[*] --> Active: BEGIN transaction
Active --> Committed: COMMIT (all operations succeed)
Active --> Aborted: ROLLBACK / error
Committed --> [*]: changes durable
Aborted --> [*]: no changes (as if never happened)
This state diagram shows a transaction's lifecycle. A transaction begins and enters the active state, where its operations run. From there it either commits - if all operations succeed, making its changes durable - or aborts, through a rollback or an error, leaving no changes as if it never happened. This all-or-nothing outcome is the essence of atomicity.
A - Atomicity: all or nothing
Atomicity means a transaction is all-or-nothing: either all its operations succeed, or none of them do. There's no partial completion. If any part of the transaction fails, the entire transaction is rolled back, leaving the data as if the transaction never happened.
In the money transfer: atomicity guarantees you can't subtract from account A without adding to account B. If the system fails after the subtraction but before the addition, atomicity ensures the subtraction is undone - the money isn't lost. The transaction either completes entirely (money moves) or not at all (nothing changes). Without atomicity, a failure mid-transaction leaves money vanished or created from nothing - the most basic corruption there is.
This is why atomicity is the property people most associate with transactions: it's the "treat these operations as one indivisible unit" promise.
C - Consistency: valid state to valid state
Consistency means a transaction takes the database from one valid state to another valid state, never leaving it in a state that violates its rules. The database's rules - constraints, like "an account balance can't go negative," or "every order must reference a real customer" - are always respected. If a transaction would violate a rule, it's rejected rather than allowed to corrupt the data.
In the money transfer: consistency ensures that defined rules hold - if there's a rule that balances can't go negative, a transfer that would overdraw account A is rejected, keeping the data valid. The total money in the system is preserved (what leaves A equals what arrives at B). Consistency is what guarantees the database never ends up in a state its own rules say is impossible.
(A note on the word: "consistency" here - valid state per the database's rules - is different from "consistency" in the CAP theorem sense of all nodes agreeing. Same word, different concept. This trips people up constantly.)
I - Isolation: concurrent transactions don't interfere
Isolation means concurrent transactions don't interfere with each other - each transaction behaves as if it were running alone, even when many run at the same time. The intermediate, half-finished state of one transaction isn't visible to others; they see the data as it was before, or as it is after, but not a corrupted in-between.
In the money transfer: imagine two transfers touching the same account simultaneously. Without isolation, they could read and write the balance in an interleaved way that corrupts it - both read the same starting balance, both update from it, and one update is lost. Isolation prevents this, ensuring the transfers don't step on each other and the balance ends up correct as if they'd run one after another.
Isolation has levels - databases offer a spectrum from strict (transactions fully isolated, as if perfectly sequential) to relaxed (some interference allowed for better performance). Stricter isolation is safer but can be slower; weaker isolation is faster but allows certain anomalies. Choosing the isolation level is a real trade-off between correctness guarantees and performance, and it's one of the more subtle parts of working with transactions.
D - Durability: it survives crashes
Durability means once a transaction is committed, it stays committed - permanently - even if the system crashes immediately after. The confirmed change is safely persisted and will survive power loss, restarts, and failures.
In the money transfer: durability guarantees that once you're told the transfer succeeded, it did - even if the database crashes a half-second later, the completed transfer is still there when it recovers. Without durability, a crash could undo a transaction you were already told had succeeded, which would make any confirmation meaningless.
Durability is delivered through mechanisms like write-ahead logging - the database records committed changes to a durable log so they survive crashes. (See the dedicated guide on how databases guarantee durability after a crash for exactly how this works.)
Isolation levels and the anomalies they prevent
Isolation deserves a deeper look, because it's the most nuanced of the four and the source of subtle bugs. The reason isolation has levels is that perfect isolation (every transaction behaving exactly as if it ran completely alone, one after another) is expensive, so databases offer weaker levels that allow more concurrency at the cost of permitting certain anomalies. The classic anomalies, in increasing order of how much isolation you need to prevent them:
- Dirty read: a transaction reads data another transaction has written but not yet committed. If that other transaction then rolls back, the first read data that never officially existed. The weakest isolation levels permit this; most practical ones prevent it.
- Non-repeatable read: a transaction reads a row, then reads it again later in the same transaction and gets a different value, because another transaction modified and committed it in between. The same query gives different answers within one transaction.
- Phantom read: a transaction runs a query returning a set of rows, then runs it again and finds new rows another transaction inserted in between - "phantoms" that appeared mid-transaction.
Stronger isolation levels prevent more of these anomalies, up to the strictest level (often called serializable), where transactions behave as if they ran strictly one at a time, preventing all of them. The trade is performance: stronger isolation means more coordination and less concurrency. Choosing the right isolation level means deciding which anomalies your application can tolerate versus how much concurrency you need - a genuine engineering decision, not a default to ignore.
Atomicity in action: the rollback
Make atomicity concrete with the mechanics of a rollback. A transaction begins. It subtracts
00 from account A - this change is made within the transaction. It attempts to add 00 to account B, but something fails (a constraint violation, a crash, an error). Because the transaction can't complete fully, the database rolls back: the subtraction from account A is undone, returning the data to exactly its state before the transaction began. The client gets an error, and crucially, no money was lost - account A still has its 00.Contrast the no-atomicity world: the subtraction happens, the addition fails, and there's no rollback -
00 simply vanishes. Atomicity is the machinery that makes "all or nothing" real: the database tracks a transaction's changes and can undo them entirely if it can't complete. This is why you wrap related operations in a transaction - so the database guarantees they either all happen or all unwind.How transactions are used in practice
You use transactions explicitly with a simple structure: mark the beginning of a transaction, perform your operations, then either commit (make all changes permanent) or roll back (discard them all). In SQL that's roughly BEGIN, your statements, then COMMIT or ROLLBACK. If anything goes wrong, you roll back and the database unwinds everything; if all is well, you commit and ACID guarantees the changes are atomic, consistent, isolated, and durable. Most database libraries wrap this so an error automatically triggers a rollback, ensuring you never accidentally leave a transaction half-done. The four ACID properties are what the database promises about this begin-operations-commit structure - the guarantees that make wrapping operations in a transaction worth doing.
Consistency, more precisely
Consistency (the C) is the most debated of the four, because it overlaps with the application's own responsibility. The database guarantees a transaction won't violate the rules it knows about - constraints like uniqueness, foreign keys (every order references a real customer), check conditions (a balance can't go negative), and the like. If a transaction would break one, the database rejects the whole transaction (which, via atomicity, rolls it back), keeping the data valid.
But "consistency" in the fullest sense - that the data is meaningfully correct for your application - is partly the application's job: the database enforces the rules you've defined, and it's on you to define rules that capture what "valid" means for your domain. So consistency is a partnership: you declare the constraints that define a valid state, and the database guarantees transactions never leave the data violating them. The stronger your constraints, the more the database protects you - which is why well-designed schemas with proper constraints are so valuable, turning the database into an active guardian of validity rather than a passive store. And the four properties reinforce each other here: atomicity ensures a rule-violating transaction rolls back completely, and isolation ensures concurrent transactions don't conspire to reach an invalid state neither would reach alone. Consistency is, in a sense, the goal the other three properties serve.
Why ACID matters
Put the four together and you have a database you can trust with critical data: transactions are all-or-nothing (atomicity), never violate the rules (consistency), don't corrupt each other when concurrent (isolation), and survive crashes once committed (durability). This is precisely what you need for anything where correctness is non-negotiable - financial systems, inventory, orders, bookings, anywhere a half-completed or corrupted or lost transaction causes real harm.
ACID is the reason relational databases are the default for this kind of data. When the data must be correct, ACID transactions are how you guarantee it - and that guarantee is doing real, constant work every time money moves, an order is placed, or inventory is decremented, even though the application code that wraps a few operations in a transaction looks almost trivially simple.
ACID vs. BASE: the other end of the spectrum
It's worth knowing that ACID isn't the only model. Many NoSQL systems, built for massive scale and high availability, relax ACID's strict guarantees in favor of an approach often summarized as BASE (Basically Available, Soft state, Eventually consistent). These systems trade strong consistency and strict transactions for scalability and availability - accepting eventual consistency (data converges over time rather than being instantly consistent everywhere) to scale across many machines and stay available.
This connects to the CAP theorem: at scale, you trade between strong consistency and availability, and ACID and BASE sit at different points on that spectrum. The choice isn't "which is better" but "which fits the data" - ACID where correctness is critical (money), BASE-style eventual consistency where scale and availability matter more and brief staleness is acceptable (feeds, counts). (See the SQL vs NoSQL and consistency-models guides.)
Tip: Emeka's note from the data trenches: The practical wisdom about ACID is knowing which data actually needs it. It's tempting to either demand ACID everywhere (and pay for it in scale you might not afford) or wave it away for everything (and corrupt data that needed protection). The skill is matching the guarantee to the data: the transactional core - payments, orders, anything where a lost or half-done operation is a real problem - gets a database with full ACID transactions, no compromise. The high-volume, low-stakes data - activity events, view counts, feeds - can live in systems with relaxed guarantees that scale more freely. In real systems I've built, both coexist: ACID where correctness is sacred, eventual consistency where scale and availability win. Knowing where to draw that line is the actual expertise.
Interview: ACID is fundamental and comes up constantly. Be able to state each property crisply - atomicity (all or nothing), consistency (valid state to valid state), isolation (concurrent transactions don't interfere), durability (survives crashes) - ideally with a concrete example like the money transfer. The stronger signal is knowing when you need ACID (correctness-critical data) versus when relaxed guarantees suffice (high-scale, low-stakes data), and that this connects to the SQL-versus-NoSQL and CAP trade-offs. That shows you understand ACID not as trivia but as a design decision.
The bottom line
ACID is the set of four guarantees that make a database trustworthy for critical data. Atomicity - a transaction is all-or-nothing, never half-done. Consistency - transactions take the database from one valid state to another, never violating its rules. Isolation - concurrent transactions don't interfere, each behaving as if alone (with isolation levels trading strictness against performance). Durability - once committed, a transaction survives any crash. Together, on the money-transfer example, they ensure money moves completely or not at all, never breaks the rules, isn't corrupted by concurrent transfers, and stays done once confirmed.
ACID is why relational databases are the default for correctness-critical data, and BASE-style eventual consistency is the scalable alternative for data where availability and scale matter more. Knowing which data needs which is the real expertise. We cover transactions and the guarantees behind them across System Design Fundamentals.
Go deeper
- ACID (Wikipedia) - the four properties defined.
- Database transaction (Wikipedia) - transactions and their behavior.
- PostgreSQL - Transactions - transactions in a real database.
- Designing Data-Intensive Applications - the definitive treatment of transactions, isolation, and consistency.