# ADR-0016: Option values are retained when variants reference them - **Status:** Accepted - **Date:** 2026-08-12 ## Context A merchandiser removes the "Red" colourway from a product. What should happen to the three Red variants and to the Red option value itself? Three things reference them and each has a different claim: - **Order history** — an order line points at a variant id, and reading that order later needs "Red / M" to still mean something. - **The variant matrix** — Red must stop generating combinations. - **The storefront** — a shopper must not see a Red swatch that can never be selected. The database already takes a position: `ProductVariantOptionValue.optionValue` is `onDelete: Restrict`, precisely so that deleting a value cannot silently orphan or cascade into history. ## Decision **Variants are archived, never deleted.** A removed combination sets `status = ARCHIVED`. The row, its SKU and its option links all survive, so an order placed last month still resolves. **Option values are deleted only when nothing references them.** If any variant — active or archived — still links to a value, the value is _retained_. It stops appearing in the matrix and stops being offered, but it continues to exist so the archived variants remain readable. **The matrix is built from the operator's submitted option set, not from the database.** Retained values would otherwise regenerate the very variants that were just archived. **The storefront filters option values to those offered by at least one active variant.** Without this last step, a retained value renders as a permanently disabled swatch with no explanation. ## Consequences Order history stays intact and readable, which is the constraint that drove everything else. Re-adding a removed colourway later reuses the retained value and its translations rather than creating a duplicate. The cost is that `product_option_values` accumulates rows that are invisible to shoppers, and "delete" is not always literally a delete — a merchandiser who inspects the database will find values they thought they removed. The schema comment and this ADR are the mitigation; a future admin screen could surface retained values explicitly. This was found the hard way: the first implementation deleted values before archiving variants and hit the `Restrict` constraint as a 500. The failure was the schema doing its job. ## Alternatives considered **Cascade the delete.** Removes the option value and its variant links, which either breaks order lines or cascades into them. Rejected — this is the outcome `Restrict` exists to prevent. **Soft-delete option values with an `isActive` column.** Functionally similar to retention, but adds a column and a filter to every catalog query for a case that is already handled by "is any active variant offering this?". Rejected as redundant state. **Refuse to remove a value that has variants.** Simple and safe, but forces the merchandiser to archive six variants by hand before they can drop a colourway — pushing bookkeeping onto the person the tool exists to help.