A team decides that order numbers only need to be unique within each store. The database uses a store ID and an order number together to find an order. That is a reasonable choice while every screen and service already knows the store. Then support asks for one search box across all stores, and a payment partner wants a single order identifier in its callbacks. The number alone can point to more than one order.
The decision is simple to revise on a design sketch. Once orders, links, exports, and integrations contain that number, changing its meaning is a different job. The extra work comes from preserving the promises that earlier versions of the system made to other people and software.
This is a fictional example. Its architecture and rollout are illustrative, not a report of a particular company's incident.
Before the identifier escapes
Suppose the team catches the global lookup requirement before launch. It can choose a globally unique, stable order ID as the machine identifier and keep a short order number for display within each store. The API and database can consistently use the global ID, while a support screen can still show the familiar store number. The team needs to check how IDs are generated, how people search, and what the partner will send back. But there are no old orders to translate and no deployed clients expecting the first contract.
Even here, the change is not free. The two identifiers serve different jobs, so the team should say which one an API accepts and which one appears on a receipt. Still, it can settle those questions within one version of the system.
After records and clients rely on it
Now imagine the store-scoped number has been in production. The Orders database contains pairs such as store A, order 418 and store B, order 418. Customer links include both values. An internal refund tool sends both to the Orders API. An export gives each store its own order numbers. The payment partner stores the pair as a merchant reference and sends it back in callbacks. These paths work under the original rule.
The new support search cannot take 418 alone and safely choose an order. A database migration that adds a global ID solves only part of the problem. Existing rows need IDs, new writes need to create them, and the team has to decide what happens when an older client still sends a store-scoped reference. The refund tool, customer links, exports, and partner callbacks may run on different release schedules. Replacing the old field everywhere at once would assume a coordinated cutover the team does not control.
A compatible transition could add a global ID without changing the meaning of the store-scoped number. The Orders API would accept the old pair where it already does, return the new ID to updated clients, and require the new ID for any lookup that lacks store context. A migration would assign IDs to existing orders and preserve the mapping from each old pair. The database would enforce that each global ID belongs to only one order, and the ID would never be reassigned. The support search could use the new ID immediately when it has one; a bare old number would need the store or another disambiguating clue. It must not silently pick one of the two 418s.
That plan creates a period with two live identifiers. It requires checks that every existing order got one global ID, that new orders do not get two, and that old links and callbacks still resolve to the same record. If the partner can only return the old pair, the callback handler must keep that lookup until the partner changes its contract and its older transactions no longer need it. If a partner callback truly contains only the number, there is no safe automatic mapping for duplicates; the team must resolve that missing context with the partner or handle those cases explicitly.
Why rollback also changes
Before launch, the team can discard the first design and use the revised one. During a production transition, "roll back" needs a more precise meaning. The team might stop routing support searches through the new lookup and return to the old search path. But once another client has stored or sent only a global ID, an older API that understands only the store-scoped pair would break that client. The global-ID lookup and its stable mapping must remain available even if the new support search is switched off. Removing or reassigning exposed IDs would also break references.
This is why a late change can spread through work that did not appear in the original request. Someone must test old and new request forms, coordinate client and partner releases, inspect historical data, and decide how long compatibility lasts. The cost comes from dependencies and state, not from the number of characters in the schema change. Some systems have few such dependencies; others have many. The example does not establish a fixed cost ratio.
What the old estimate can and cannot tell us
In their 2001 Software Defect Reduction Top 10 List, Barry Boehm and Victor Basili described fixes after delivery as often costing 100 times as much as fixes during requirements and design. In the same discussion, they said the escalation for small, noncritical systems was nearer 5:1, and that good architecture practices could reduce it even in large, critical systems. This was a historical synthesis about software defects, not a current measurement of identifier migrations or a rule for every project. It does not price the fictional change above.
An early sketch in Lycana can trace the Orders API, support lookup, and partner callback with the identifier each path carries. If one arrow can mean either a store-scoped number or a global ID, settle that meaning before the partner stores it.
The useful decision is narrower than that headline number. When a proposed field or assumption will appear in persisted records, public links, or another organization's API, ask what its meaning is and who will depend on it. If the answer is still uncertain, keep its scope explicit and leave room for a second identifier or a versioned contract. After it ships, plan the change around both populations of data and clients. The point of the early review is to find the promises that will be hardest to revise, while they are still easy to name.


