Lorixen.
EN

What Should Happen When Two People Edit the Same Record in a Small Business Web App?

For any record where a lost change matters, such as a reservation, a quote or a stock count, the server should refuse to save an edit that was based on an out of date copy. The app should then tell the person in plain words that someone else changed the record first, keep everything they typed, and let them reload, compare and reapply. For records where a lost change is cheap, the owner may deliberately choose that the latest save simply wins. What the app should never do is overwrite silently by accident. The policies below are proposals for a commissioned app, meant to be adjusted by the owner.

What a stale edit is

A stale edit happens when someone saves changes made on a copy of a record that is no longer current. Two staff open the same record. One saves. The other saves a minute later from a screen that still shows the old values. If the app accepts the second save as it is, the first change disappears and nobody is told.

This is a different problem from three nearby ones:

  • A duplicate action is the same save arriving twice, for example from a double click or a retry. The fix is about recognising repeats, not about comparing versions.
  • A permission problem is someone saving something they are not allowed to change. Roles decide that, not timing.
  • Database transaction behaviour is about how the database handles work happening at the same moment. It does not know that a person has been looking at a form for ten minutes.

A stale edit can involve none of these. Both people are allowed, each clicked once, and the data is still wrong afterwards.

Last write wins is a business choice

Last write wins means the most recent save replaces what is stored. It is not a universal bug. For a display name or a colour tag, it can be exactly right, because nobody is harmed if the newest value stands. For a reservation or a quote, it can quietly destroy someone's work.

The useful question for each record type is: which is worse, a lost change or an interrupted save? Write the answer down. Then the app behaves on purpose instead of by accident.

A concrete stale save sequence

This is a hypothetical equipment reservation, invented for illustration.

  1. Ana opens reservation R for a projector: Tuesday 09:00 to 11:00, status Confirmed, note says Collect from front desk. The record is at version 4.
  2. Ben opens the same record in another tab. He also sees version 4.
  3. Ben changes the time to 13:00 to 15:00 and saves. The record becomes version 5.
  4. Ana, still on the version 4 screen, changes only the note to Collect at 08:45 and clicks save.

If the form sends every field and the app accepts it, Ana's save puts the time back to 09:00 to 11:00. Ben's change is gone and neither person is warned. With a version check, the server compares the version Ana's screen was based on (4) with the stored version (5), sees a mismatch and does not save.

Proposed owner policy for the example

  • Time, equipment and status changes: always prompt on conflict. Never overwrite silently.
  • Note field: also prompt, because the note may explain the time change. Automatic merging is optional and covered below.
  • Delete while editing: if one person deletes the record while another edits it, the editor's save is refused with a message that the record no longer exists. Their typed input stays visible so they can copy it or create a new record on purpose.

Now the question of other records. A version check protects one record. It does not stop Ben from moving reservation R onto a slot that overlaps a different reservation S for the same projector, and it does not stop two people saving two different records that together double book the projector. That is a separate booking rule. The proposal is that the server enforces the no overlap rule on every save of any reservation by checking the other reservations for that equipment, and shows its own clear message when the rule fails. The version check and the overlap rule are two jobs, and both need to be specified.

What a good conflict message does

A useful prompt is short and specific. It says the record changed, when, and ideally who changed it. It offers clear choices:

  • Reload the latest version and show it beside what I typed.
  • Reapply my changes field by field, confirming each one.
  • Copy my input and cancel.

Preserving unsaved input matters more than almost anything else here. A conflict message that wipes the form turns a small annoyance into lost work. The app should also not resend the person's edit automatically after a mismatch. They need to see the new values first, because their change may no longer make sense.

Field merges and editing locks

Field level merging means that if Ana changed only the note and Ben changed only the time, both changes are kept. This is tempting but risky, because fields that look independent often are not. A note saying Collect at 08:45 is wrong once the time moves to the afternoon. A cautious proposal: merge automatically only for fields the owner has listed as independent, and prompt for everything else.

An editing lock is another design option. When someone opens a record, others see that it is being edited, and the lock ends after a set time or when the form closes. Locks add their own questions. What happens when someone leaves a tab open? Who may override an expired lock? These are product decisions for the owner, not facts about any platform. Even with locks, the save check below should stay, because a lock can expire while a form is still open.

The check must be enforced by the server

Refreshing the page on a timer, greying out a Save button or showing a warning banner only helps the browser. A second tab, a slow connection or a direct request can still send an old save. The server must compare versions on every save and refuse mismatches, whatever the screen did or did not show.

There is a further requirement, proposed here as part of the specification. The comparison and the write must happen as one atomic conditional operation: save this change only if the record is still at the version the person saw. If the app instead reads the current version, compares it, and then performs a separate unconditional write, another save can land in the gap between the two steps and still be overwritten. The brief does not need to prescribe how a developer achieves this, only that the check and the write cannot be separated and that the result is testable with two overlapping saves.

What the technical sources do and do not say

HTTP has a standard way to express a version check. MDN explains that If-Match can be used to prevent the lost update problem, and that if the condition does not match, a 412 Precondition Failed response is returned. MDN's page on that status gives a wiki example in which mismatched hashes mean the document was edited in between and a 412 error is thrown. An app may use this mechanism or store its own version number. Either way, the owner should specify what the person sees afterwards. A version header alone does not guarantee a correct workflow. It says nothing about the cross record overlap rule, about permissions, or about keeping the person's input.

Database behaviour is specific to each product and setting. The PostgreSQL documentation states that Read Committed is the default isolation level in PostgreSQL, which describes that database's default and not every app. The same page notes that in that mode two successive SELECT commands can see different data within a single transaction. In general terms, a transaction groups database work together, and the guarantees it gives depend on the isolation level and the database. A transaction wrapped around only the final save does not automatically compare the version the person originally loaded, so it does not replace a version check.

Acceptance checklist

These are proposed checks to put in the brief. For help with phrasing, see how to write acceptance criteria for a small web app.

  • Two tabs, same user: save in tab A, then save the stale tab B. B is refused with a clear message.
  • Two users: the same result, and the message names the other editor if the owner wants that.
  • Delayed save: leave a form open until the record has changed, then save. It is refused.
  • Overlapping saves: two saves sent at nearly the same moment cannot both succeed against the same starting version.
  • Input retained: after a refusal, everything typed is still visible and can be copied.
  • Reload, compare, reapply: the person can see old and new values and reapply deliberately.
  • Permissions rechecked: on reapply, the server checks the person's current permissions again, in case roles changed in the meantime.
  • Delete while editing: the save is refused with a message that the record is gone.
  • Cross record rule: moving one reservation onto another reservation's slot is refused, even when its own version is current.
  • No automatic replay: nothing is resubmitted until the person confirms.
  • Test evidence is recorded using test records, and reports state only what was actually run.

Frequently asked questions

Is last write wins ever acceptable?

Yes, when the owner has decided that a lost change is cheap and an interrupted save is annoying. Low stakes personal settings are the usual example. The key word is decided. It should be a listed choice for named record types, not the result of nobody having thought about it. For anything where another person's work could vanish, the safer default is to refuse the stale save and explain why.

Does using a database transaction solve this?

Not on its own. A transaction groups database work, and what it guarantees depends on the isolation level and the database in use. A stale edit happens over minutes, between a person opening a form and saving it, and a transaction around only the final save does not compare the version that person first loaded. You still need the version check, enforced together with the write, and you still need your own rules, such as no double booking, checked on the server.

Should the app merge changes automatically?

Only for fields the owner has agreed are independent, and only if the person can see the result. When in doubt, prompt. A wrong silent merge is harder to notice than an obvious conflict message, so for a small team the safer default is to show both versions and let the person reapply.

Next step

If you are commissioning a shared app, bring three things to the conversation: the lost change or latest wins choice for each record type, the rules that span several records, and the checklist above. You can describe your project on the work with me page.

WhatsAppLet’s talk