CASE STUDY · CLIENT NAME REMOVED
A training lesson of a web app
One role, one date, one directory: map and format shared data in one place
These defects all come from the same problem: the same fact was worked out in several places, and the copies drifted apart. A role was read one way at sign-in and another way elsewhere, dates were taken from the wrong clock, and people and roles were shown as stored rather than as a person would read them.
What went wrong, and how to avoid it
- Managers and senior managers signed in as Auditors, with the Auditor menus Why: Sign-in had its own role mapping that sent every role it did not name to the most restricted role, while the shared mapping used when opening other books did not agree with it.Avoid it: Keep a single mapping from role id to shell or permissions, and have every entry point call it. Add tests for every known role so a new role cannot fall through a default unnoticed.
- Captured time said Today over a past day, and worked out today in UTC Why: Labels were fixed words regardless of the date shown, and the check for today used the UTC date rather than the user's local calendar day.Avoid it: Derive labels from the data being shown, and use one shared helper for 'today' that works on the local calendar day. Test with a user in a non-UTC time zone.
- The dashboard greeted newly added staff by their internal user id Why: A per-process read model cached the firm's people directory forever, so anyone added after the first read was missing until a restart.Avoid it: Give caches a short expiry or invalidate them when people or roles change. Always have a readable fallback for display data, and never use a raw id in a user-facing greeting.
- People lists showed SeniorManager as a raw code, and the timer panel opened off-screen Why: Several people lists printed role ids as stored instead of in words, and the popover was anchored to a fixed edge without checking the window.Avoid it: Format role ids in words through one shared function used by every list and API that shows people. Place popovers in code that keeps them inside the viewport, and test at the smallest supported screen size.