Skip to content

Lesson 06 — Why database transactions cannot control the outside world

Outcome

Add the Operation concept and use a fake external effect to understand durable intent.

Why this comes now

The next milestones touch Git and processes. Before that, learn the failure mode that causes many orchestrators to get stuck after crashes.

Understand

SQLite can atomically change SQLite. It cannot atomically start Claude, create a directory, or run Git. If you mutate DB state and then perform I/O, a crash between them creates disagreement. If you perform I/O first and DB fails, you get the opposite disagreement.

Forge records an Operation in the same transaction as the domain intent, commits it, then a worker performs the side effect. “Command accepted” means intent is durable—not that the outside world finished.

Build the real project

  1. Add Operation types: kind, aggregate target, idempotency key, status, attempt, timestamps, structured payload/result/error.
  2. Add operations table/migration/repository.
  3. Create a simple application command that updates a harmless domain/test record and inserts a fake WRITE_MARKER Operation transactionally.
  4. Prove that the Operation exists even before a worker runs it.
  5. Document which future actions will become Operations.

Completion gate

A test commits domain change + Operation atomically. Inject a transaction failure and prove neither commits. Explain in a test name/comment why the external effect is not run inside the transaction.

Pitfalls to avoid

Do not build Kafka-style infrastructure. Do not turn every database update into an Operation. Operations exist for external side effects/recovery boundaries.

References

OPERATIONS_AND_RECOVERY.md, T3 Code architecture: https://github.com/pingdotgg/t3code/blob/main/docs/internals/overview.md

Checkpoint

Write one short Learning Log note describing a crash boundary you now understand better.

Forge is local-first. The docs are part of the product engineering system.