What (ACID) Atomicity Actually Means
By the end, you should understand what atomicity guarantees, what it protects against, how databases actually implement it, and what its costs are.
Most data professionals can recite the ACID acronym. Atomicity, Consistency, Isolation, Durability. The four properties that define what a “real” database guarantees about transactions. Far fewer can describe what each letter actually means under detailed questioning.
This article is about the first letter. And by the end, you should understand":
what atomicity guarantees
what it protects against
how databases actually implement it
what its costs are
and what it doesn’t include.
The goal is to replace memorized vocabulary with real understanding.
I’m writing this for the reader who wants to genuinely understand these concepts from the ground up. If you already know what write-ahead logging is and can explain why two-phase commit is hard, this article will be too elementary. If you’ve used the word “atomic” without being sure you could defend it, you’re the intended audience.
The plain definition
Atomicity is the guarantee that a transaction either takes effect entirely or has no effect at all. There is no partial state. There is no in-between: some operations succeeded, and others didn’t.
The word “atomic” comes from Greek — atomos and means “indivisible.” A transaction in an atomic system is indivisible in the same sense: you can’t see it half-done. From the outside, it either happened or it didn’t.
This is the entire concept. The depth is in what it takes to actually make this guarantee true.
Why this guarantee matters
Here’s the popular example: moving money from your checking account to your savings account. The operation has two steps:
Subtract $500 from checking account
Add $500 to savings account
These are two separate operations on two separate rows in the accounts table. The database has to do both together to avoid inconsistencies.
Now imagine something interrupts the database between step 1 and step 2. The server crashes. The power goes out. A bug in the application code throws an exception. Whatever the cause, the second step never happens.
Without atomicity, here’s the state of the world:
Your checking account has $500 less
Your savings account is unchanged
$500 has vanished
The database is in a state that should be impossible. The total balance across your accounts is wrong. From your perspective, the bank stole $500. From the bank’s perspective, the books don’t balance. Both are right; both are problems.
This is the failure mode atomicity protects against: partial completion of a logical operation that was supposed to be all-or-nothing.
The atomicity guarantee says: if the database commits to this transaction, both steps happen. If the database can’t commit (for any reason — crash, bug, conflict), neither step happens. The world either moves cleanly from “before the transfer” to “after the transfer,” or it stays in the “before” state. There is no third option where one step happened and the other didn’t.
This is the foundation. Everything that follows is about how to actually deliver this guarantee in a world where computers crash, networks fail, and writes don’t always reach disk.
Why this is harder than it sounds
You might think: “Just do both operations together. How hard can it be?”
The difficulty is that storage doesn’t natively support multi-step atomic operations. Writing to disk, like everything in computing, happens in small, discrete steps. The CPU writes some bytes. The operating system flushes them to disk. The disk controller stores them somewhere. Each of these steps can fail independently.
Imagine you’re implementing this transfer without any help from the database. Your code would look something like this:
balance_checking = balance_checking - 500
write_to_disk(account_checking) # <-- crash could happen here
balance_savings = balance_savings + 500
write_to_disk(account_savings)If a crash happens at the marked point, you have the exact problem we described. The first write is committed to disk; the second never happens. There’s no way for the code to “undo” the first write after the crash, because the code itself isn’t running anymore. The application is dead; only the disk remembers what happened.
This is the fundamental challenge: you need to make a guarantee about multiple operations, but each individual operation has independent failure modes. The system has to be designed so that any failure at any point leaves the database in a recoverable state.
The standard solution is called write-ahead logging, and it’s the mechanism behind atomicity in nearly every modern database. Understanding it is the difference between knowing what atomicity is and knowing how it works.
How write-ahead logging actually works
The core idea is elegant: before you make any change to the actual database, write down what you’re going to do in a separate log file.
The sequence for our transfer:
Write to the log: “Transaction T1 starts. T1 will subtract 500 from checking and add 500 to savings.”
Flush the log to disk. Make sure those instructions are durably written before doing anything else.
Apply the changes to the database. Update the checking row. Update the savings row.
Write to the log: “Transaction T1 committed.”
Flush the log to disk.
If a crash happens at any point in this sequence, the database can recover correctly. Let me walk through each case:
Crash before step 2 (log flush). The log doesn’t have a record of T1 starting. On recovery, the database doesn’t know T1 ever existed. It’s as if T1 never happened. The database is in the “before” state — which is correct, because the transaction hadn’t really started yet.
Crash between step 2 and step 5. The log has “T1 will do X and Y” but no commit record. On recovery, the database sees an incomplete transaction. It can either replay the operations to finish T1, or roll back by undoing any partial changes. The choice depends on database policy, but either way the result is consistent — either both operations are applied, or neither is. There’s no partial state.
Crash after step 5. The log has both the operations and the commit. On recovery, the database knows T1 completed. Any database changes that didn’t make it to disk before the crash can be replayed from the log. The database ends up in the “after” state — also correct.
The key property: the log is the source of truth. The database files might be in an inconsistent state after a crash, but the log tells you exactly what happened and how to recover. The actual data files can be rebuilt from the log if necessary.
This is why “write-ahead” matters in the name. The log is written before the actual database changes. If you wrote the database changes first and then the log, a crash between them would lose information — you’d have changes you couldn’t track. By writing the log first, every change is recorded before it’s applied.
There’s a subtle but crucial detail here. Step 2 — flushing the log to disk — is what makes atomicity possible. The database isn’t allowed to start changing the actual data until it knows the plan is durably recorded. If the log is only in memory when the crash happens, the database loses both the changes and any way to track them. The flush is the moment the transaction becomes recoverable.
The cost of all this
Write-ahead logging is elegant but not free. The costs are real and worth understanding.
Every transaction does extra writes. Without WAL, you write the data once. With WAL, you write the log entry first, then the actual data. That’s roughly twice the write volume. Modern databases optimize heavily — they batch writes, defer some data file updates, and use various other tricks — but the fundamental cost is still there.
Commits require disk flushes. When the database says “your transaction is committed,” it has to be sure the log entry survives a crash. This means calling fsync() — telling the operating system to actually write the data to durable storage, not just to its in-memory buffer. fsync is one of the slowest operations in computing. A modern SSD can do hundreds of thousands of writes per second but only a few thousand fsyncs per second. The cost of durability is real.
Recovery takes time. After a crash, the database has to read the log and replay or roll back transactions. The longer the log, the longer recovery takes. Databases mitigate this with periodic “checkpoints” — snapshots of the state that let recovery start from a known point — but recovery time is still proportional to log length since the last checkpoint.
Concurrent transactions interact. When multiple transactions run at once, the log has to record their operations in an order that allows correct recovery. This adds coordination overhead that grows with concurrency.
These costs are why databases offer various ways to relax durability for performance:
Group commit: Batch multiple transactions’ commits into a single fsync. Each individual transaction sees slightly higher latency, but throughput improves dramatically.
Asynchronous commit: Return success to the client before fsync completes. Much faster commits, but committed transactions can be lost on crash. Postgres calls this
synchronous_commit = off.Unlogged tables: Skip WAL entirely for specific tables. Postgres supports this. The table can be lost on crash, but writes are very fast.
The pattern worth noticing: atomicity is implemented by trading throughput and write latency for crash safety. Every database makes this tradeoff somewhere, and most expose knobs that let you choose where on the spectrum to operate. The defaults are usually safe; the relaxations are for specific workloads where you’ve decided the safety cost is acceptable.
What atomicity is not
This is the part that separates surface understanding from real understanding. Atomicity guarantees one specific thing — that a transaction completes or doesn’t. It’s not about anything else. Several things people commonly assume atomicity covers, but it doesn’t:
Atomicity doesn’t mean immediate visibility to other transactions. A transaction can be atomic and still not be visible to other concurrent transactions until it commits. The visibility question is isolation, the third letter of ACID. Atomicity and isolation are related but distinct. A transaction can be perfectly atomic and still produce surprising results when running concurrently with other transactions, depending on the isolation level.
Atomicity doesn’t mean correct. A transaction can be perfectly atomic and still be wrong. If your application has a bug that subtracts $500 from checking and adds $50 to savings, atomicity guarantees both operations happen — but the result is wrong. Atomicity is about the integrity of the operation, not the correctness of the logic. The database can’t protect you from bad code; it can only protect you from partial completion of whatever code you wrote.
Atomicity doesn’t extend to external systems. If your transaction also needs to send an email confirming the transfer, atomicity doesn’t help with that. The database operation is atomic; the email is a separate system. You can commit the database changes and fail to send the email, or send the email and fail to commit. Coordinating across systems requires more than atomicity — typically distributed transactions or careful application design.
This last point is the source of a huge class of bugs in real systems. Application code often assumes “the database write succeeded, so the external action also happened.” Without explicit coordination, that assumption is wrong. Atomicity covers what happens inside the database, not what happens after the database tells you it succeeded.
Atomicity doesn’t extend across multiple databases by default. A transaction in Postgres is atomic within that Postgres instance. If your application updates both Postgres and another database in a logical operation, you have two independent atomic transactions, not one. Either can succeed while the other fails. Making transactions atomic across multiple databases requires distributed transaction protocols like two-phase commit, which are expensive and operationally fragile.
The pattern: atomicity has a boundary. Inside that boundary, you can rely on all-or-nothing semantics. Outside it, you need to design around the fact that operations can partially complete. The skill is knowing where the boundary sits for each system you work with.
What’s worth taking away
Atomicity is the simplest of the four ACID properties to state and the foundation for the others. It’s the guarantee that a transaction either fully happens or doesn’t happen at all — no in-between state where some operations succeeded and others didn’t.
The implementation rests on write-ahead logging: writing down your plan before executing it, so that any crash can be recovered correctly. The log is the source of truth; the actual database files are derived from it. This costs throughput and write latency, but those costs are what buy you the guarantee.
What atomicity is not: it’s not isolation, it’s not correctness, it’s not coordination with external systems, and it’s not a transaction guarantee across multiple databases. Each of these is a different concern, often with different solutions.
The practical takeaway: when you use a database with ACID guarantees, atomicity means you don’t have to worry about partial state within a transaction. When you cross a system boundary — to another database, to an external API, to a file system, to a message queue — atomicity no longer applies. You’re now responsible for designing around the fact that things can partially complete.
This boundary is where most production bugs in data systems live. Understanding it precisely is what separates engineers who can reason about correctness from those who can only hope for it.
Sources
Foundational:
Gray, J. (1981), The Transaction Concept: Virtues and Limitations — the original framing of ACID and transactions
Mohan, C. et al. (1992), ARIES: A Transaction Recovery Method Supporting Fine-Granularity Locking and Partial Rollbacks Using Write-Ahead Logging — the foundational paper on modern WAL implementation
Practical:
PostgreSQL Documentation, Reliability and the Write-Ahead Log
Designing Data-Intensive Applications by Martin Kleppmann, Chapter 7 (Transactions) — the most accessible modern treatment
System-specific:



