Skip to main content
All posts
Engineering· by Prasad W.

One Writer Per Table: Write Ownership in an ERP

Read-many, write-one as an architecture rule: any module may read any table, but only the owning module may write to it. Why that makes bugs findable.

Short answer. Any module in the system may read any table. Each table has exactly one module allowed to write to it. That single constraint decides how long it takes to find a bug and how far a bad one can travel. Disclosure: we build ERPClaw and this is one of its load-bearing rules, so weigh the framing accordingly.

The Google Doc problem

Picture a document eleven people can edit. Something in it is wrong. To find out how it got that way you open the version history and start guessing: who changed this, when, and were they reacting to somebody else’s change.

Most enterprise systems are that document. Several modules can write to the same table because at some point each of them had a good reason, and the reasons accumulated. Then a sales invoice comes out wrong and the investigation begins by asking which of eleven code paths touched it.

The cost is not the bug. The cost is that finding the bug requires understanding the whole system rather than one part of it, and that cost is paid on every bug, forever.

The rule

Two halves, and both matter.

Any module may read any table. Reads are free and unrestricted. A module that needs to know a customer’s credit limit or an item’s on-hand quantity simply looks. No permission negotiation, no data duplication, no synchronisation job that runs at 3am and sometimes fails.

Exactly one module may write to each table. The owning module and nothing else. Where another module needs a change made, it asks the owner rather than reaching in.

Read-many keeps the system integrated. Write-one keeps it debuggable. Systems that get this wrong usually get it wrong in one direction: locking down reads, which produces data duplication and reconciliation work, or opening up writes, which produces the Google Doc.

What it buys, concretely

One place to look. A sales invoice is wrong, so you open the module that owns sales invoices. Not a search across the codebase, not a trace through eleven candidates. One file. The time between “this is wrong” and “here is the line” collapses, and that gap is where most debugging time actually goes.

Contained blast radius. A defect in the healthcare module cannot corrupt the sales ledger, because it never had write access to it. Not through policy or code review or a test that someone remembered to write, but because the permission does not exist. This is the property that makes generated modules safe to add: a new vertical arrives with its own tables and no ability to damage anything that was already working.

Reviewable change. When a table has one writer, the diff that changes how it is written is in one place, and the reviewer knows what they are looking at. When it has six, the reviewer has to reason about interactions they cannot see.

What it costs, because it does cost something

Three honest costs.

Cross-module operations need a path. When a workflow spans several modules, one of them owns each write, and the sequence has to be expressed. That is more design work up front than letting everything write everywhere.

The ownership map has to be maintained. A table whose owner is ambiguous is worse than no rule at all, because people will assume and the assumption will be wrong. Ownership has to be explicit and it has to be enforced, not documented and hoped for.

Some genuinely shared entities are awkward. Occasionally two modules both have a legitimate claim to write something, and resolving it means either splitting the table or accepting that one owns it and the other asks. Neither is free. We wrote about a narrow exception in the same spirit: a foundation table may carry a nullable reference to an entity another module owns, and only the owner may populate it.

Why an AI-run system needs it more, not less

The rule is good practice in any system. It becomes structural when an AI agent is doing the writing.

A human developer who reaches into another module’s tables is doing something they know is a shortcut. They remember it, and someone catches it in review. An agent generating a module from a specification has no such instinct; it will write wherever the schema permits. So the permission model has to be the constraint, because the discipline cannot be.

That is the same reasoning behind checking invariants after every ledger operation rather than trusting the code that just ran. In both cases the system assumes the writer might be wrong and arranges things so that being wrong is survivable. We wrote about the invariant half of that argument in why an AI cannot be your accounting system on its own, and about what the ownership rule makes possible in adding an industry is a recipe, not retraining.

The verdict

Read-many, write-one is not an elegant idea. It is a boring constraint that makes a specific bad afternoon shorter: the one where something is wrong in the books and nobody can say which part of the system did it. In a system where the writer is an agent rather than a person, boring constraints are the ones worth having.

This is one of the explanations we developed for the IEEE International Conference on Information Reuse and Integration in July 2026, where it drew more questions than we expected.

Tagsarchitectureownershiperpai-nativedebugging