A transaction groups a sequence of statements so they all succeed together or none of them takes effect. It's the feature you'd miss most if Postgres took it away — the one that keeps your data consistent when something halfway through goes wrong.
The seed has an accounts table with three owners, each starting at 100. We'll use it to write the textbook "transfer money" example and watch transactions actually do their job.
The implicit transaction
Every statement you've run so far has been wrapped in its own transaction — Postgres calls this autocommit. INSERT INTO ... is really BEGIN; INSERT INTO ...; COMMIT;. You haven't had to think about it because each statement either succeeds or raises an error before changing anything visible.
The moment you have two statements that must both succeed or both fail, autocommit isn't enough.
BEGIN ... COMMIT
The shape: BEGIN; opens a transaction, your statements run inside it, COMMIT; makes them durable.
BEGIN;
UPDATE accounts SET balance = balance - 20 WHERE owner = 'ada';
UPDATE accounts SET balance = balance + 20 WHERE owner = 'grace';
COMMIT;
Inside the transaction, the two UPDATEs are not yet visible to other connections. Only at COMMIT; does the world see both changes simultaneously. If something failed between them — a constraint violation, a network drop, the database crashing — neither change would have landed.
ROLLBACK: undo, on purpose
ROLLBACK; discards every change made since the matching BEGIN. Useful as a tool — and essential when you realize an UPDATE was about to be too broad.