CS/03 · Case study
Designing in the codebase
I’m not a core platform developer, but I did my design work inside the product’s codebase: against real data, in small reviewable pull requests, and in prototypes engineers could open without me.
Details generalized under NDA. Every visual is recreated with fictional data.
- Role
- Product designer and design engineer
- Scope
- Prototypes, production UX changes, beta analytics, table analysis
- Context
- AI reporting assistant for a government data platform, in beta
- Year
- 2026
Why code
I set up the full local environment and learned to run the front end against a shared staging environment, so I could design against real data and the live agent instead of static mocks. I had no backend ownership, and kept it that way on purpose.
How I worked
Start small
I shipped copy first (the entry screen and a contextual input placeholder) to learn the ticket → branch → PR workflow before anything bigger.
Prototype where nothing depends on it
Bigger ideas went into disposable prototypes: isolated routes nothing else depends on, deletable in one move. A small fake store let me design with services down, without locking engineers into a storage design.
Make it demoable
Agent-free deep-link routes let engineers open any flow without a live AI session. Static interactive mocks, including an offline bundle, went on tickets so reviewers didn’t need the environment.
Loop fast
I demoed prototypes to engineers and folded their feedback into the prototype, the diagram and the docs, often the same day.
Hand off in pieces
Handoffs went from one long doc to a short entry point plus a one-page ticket per component, after developers told me the first version was too long.
Decision 1
Scope the work to where the product is going
- Considered
- Build the canvas empty state the ticket described, pointing at the manual builder
- Tag every surface for analytics
- Chose
- The builder had been discontinued, so I built the empty state for the chat panel instead. For analytics I deliberately left deprecated or about-to-be-reworked surfaces untagged.
- Why
- Designing or instrumenting surfaces that are going away wastes effort and pollutes the data.
Beta analytics
I defined and tagged the beta’s user actions (prompt submitted, report created, widgets added, updated or removed, and sharing outcomes) with a typed event helper and test IDs on menus and controls. I cut the tests down to the ones that protect app behavior, because tracking must never break the app. It merged after code review and gave the beta its first custom events.
Decision 2
Frame the table work by use case, not feature list
- Considered
- A feature-parity checklist against the legacy table
- Real public-sector reports
- Chose
- I organized the parity analysis around ten kinds of public-sector report and mapped 28 table capabilities against them.
- Why
- Each report type maps to the capabilities it actually needs, so the conversation is about real reports instead of a checklist.
It surfaced something the team didn’t expect: the assistant was already ahead of the legacy tool on computed columns, date bucketing and top-N. The gap was presentation.
Outcome
- The analytics work merged and gave the beta its first custom events.
- The entry-point prototype (a create-menu card, a nav item and a profile callout) went to a developer to productionize.
- I followed up with why the current table component won’t reach parity.
What I learned
Make prototypes demoable without dependencies.
The agent-free deep links should have come first. For a while the publish wizard couldn’t be seen at all without a live agent.
Frame by use case, not feature list.
It made the table conversation legible to non-engineers.