Brief
You have inherited the inventory system of the Gilded Rose, a small inn that buys and
sells goods. Every item has a sellIn (days left to sell it) and a quality value;
once a day the system updates both for every item. The previous developer is gone; the
code is one dense conditional thicket that nobody dares touch, and there are no tests.
The goblin in the corner owns the Item class and will not let you change it. The
business has just sold a new category — conjured items — and needs the system to
support them. Your job is not merely to add the feature: it is to run the whole
episode as a managed debt-repayment cycle, producing the estimates, tests, commits,
and documents a well-run team would keep.
The domain rules (the legacy behavior you must preserve)
- Each daily update:
sellIndecreases by 1, andqualitychanges per the rules below. - Ordinary items lose 1 quality per day; once the sell-by date has passed
(
sellIn< 0), degradation doubles (2 per day). qualityis never negative, and never rises above 50 — with one exception below.- Aged Brie gains quality as it ages instead of losing it (and, per the existing code's actual behavior, gains twice as fast after the sell-by date — the tests you write will tell you; bugs included is the contract).
- Sulfuras, Hand of Ragnaros is legendary: never has to be sold, never changes —
sellInuntouched,qualityfixed at 80 (the exception to the 50 cap). - Backstage passes gain quality as the concert nears: +1 normally, +2 at 10
days or fewer, +3 at 5 days or fewer, and drop to 0 after the concert
(
sellIn< 0). - New requirement — Conjured items degrade twice as fast as ordinary items (2 per day before sell-by, 4 after), respecting the same 0 floor.
Instructions
Work through the five phases in order; do not peek ahead (in particular, do not start reading the code "for the tests" before phase 1's estimates are sealed).
Phase 1 — Sealed estimates
- Pick a language from the kata repo and set the project up so it compiles/runs.
- Read the legacy update method cold — no refactoring, no test-writing yet.
- Write down two range estimates (best case / worst case, in hours):
- E1: implementing conjured items directly on the legacy code, now;
- E2: implementing conjured items after a cleanup, including the cleanup. For each, note in two or three lines what drives the range (what scares you).
- Seal them: commit the estimates file before any other work. It is not edited again until phase 5 — mispredictions are data, not embarrassment.
Phase 2 — Golden master
Pin the current behavior, bugs included, with a characterization harness:
- Drive the system over a wide input grid — every special item name plus ordinary
ones,
sellInandqualityvalues crossing every boundary (negative, 0, 1, 5, 6, 10, 11, 49, 50, 80), updated over enough days to cross the sell-by line — and capture the full output as text. - The captured output is recorded, reviewed once by eye, then committed as the approved master; from then on the test fails on any diff. (The repo ships text-based/approval test support in most languages — use it or hand-roll the compare.)
- Rules of construction: the master must be deterministic (no timestamps, no ordering wobble); assert on observable outputs only (names, sellIn, quality); don't "fix" anything the master captures — the current behavior is the spec.
- Check coverage (line/branch) of the update logic; extend the grid until the legacy method is fully covered.
Phase 3 — Repay in micro-commits
Refactor toward a design that makes item-specific behavior explicit (polymorphism, strategy map, rule table — your call), under these cadence rules:
- Every commit ≤ ~5 minutes of work and one refactoring idea (rename, extract method, invert condition, lift special case…); the golden master is green at every single commit.
- Commit messages name the debt retired, honestly: "extract per-item update — removes 3-deep nesting on Brie path", not "wip".
- If a step goes red and the fix isn't obvious within a couple of minutes, revert, don't debug — take a smaller step.
- You may not modify the
Itemclass or its fields (the goblin's constraint) — the design must absorb this.
Phase 4 — Ship conjured items
- Implement the conjured-items rule on the cleaned design, tests first this time (real unit tests for the new behavior, alongside the master).
- Record the actual time for the cleanup (phase 3) and the feature (phase 4) separately.
Phase 5 — Retro and business case
Produce three documents (versioned with the code):
- Estimate audit — the sealed E1/E2 vs. actuals: cleanup cost, feature-on-clean cost, and your honest guess of feature-on-legacy cost in hindsight. State the measured "interest rate" in one sentence: what the mess added to the price of one feature.
- Debt records — ADR-style entries, written as if you'd inherited the codebase mid-loan: for each major debt you found (at minimum: the mega-conditional, the string-typed item identities, the missing tests), record its Fowler quadrant (deliberate/inadvertent × prudent/reckless), the interest it was charging, and how (or whether) it was repaid.
- Business case — one page, addressed to a non-technical product owner: what this debt cost (N), what the cleanup cost (M), when it paid back, and what capacity you are asking for going forward. Numbers from your own measurements, not folklore.
Constraints
- The
Itemclass and its properties are untouchable, and you may not remove the shared items array — the kata's canonical constraint. - No behavior changes before the golden master is complete — bug-for-bug compatibility is the phase-2 contract.
- The sealed estimates are never revised after phase 1; the retro compares against them as committed.
Acceptance
Mapped to the exercise's milestones:
- Estimates sealed — a committed estimates file (two ranges + rationale) whose commit predates every test and refactoring commit in the history.
- Golden master — mutation check: sabotage any single rule branch in the legacy code (flip a comparison, change a constant) and the harness fails; coverage of the update logic is total; two consecutive clean runs produce identical output.
- Micro-commits — the log from master-complete to cleanup-done is a chain of
small green commits, each naming its refactoring; no commit mixes refactoring
with behavior change;
Itemis untouched throughout. - Feature — conjured items degrade 2/day before sell-by and 4/day after, never below 0; all pre-existing behavior still matches the master; actual times for phases 3 and 4 are recorded.
- Retro — the three documents exist and are self-supporting: the audit's numbers trace to commits/timestamps, every debt record carries a quadrant and an interest statement, and the business case fits on a page and quotes only measured numbers.
Related
- Gilded Rose: a technical-debt workout — the exercise note this is the subject of.
- Gilded Rose Refactoring Kata (Emily Bache) — the kata repo: starting code in ~40 languages and the original requirements text the domain rules above restate.