Decision 01
Putting the dashboard state in the URL
Problem
Decision
Trade-off
Result
Case studyData Products2022
An application analysts use to explore energy consumption and generation across sites, meters and long time ranges, through charts and tables that stay in step with each other.
Consumption · logarithmic
MeasuredBaselineSites
Meters
Interval
Interface recreated for portfolio purposes.
The complete case study in seven parts. The sections below provide the engineering detail behind this summary.
Problem
Context
Constraints
My role
Approach
Solution
Outcome
An analytics product for exploring energy data (consumption and generation across sites, meters and long time ranges) through linked charts and tables.
The people using it are analysts. They compare series over time, drill into anomalies, and export what they find into reports. They will notice if a chart quietly rounds something away.
01
Set filters
Sites, meters, date range
02
The address updates
The view becomes a link
03
Request
Cached, and never sent twice
04
Summarise
Bounded points, however wide the range
05
Draw
Charts and tables from the same data
06
Drill in
Anomalies, and the rows behind them
07
Export
CSV, PDF or image of exactly this
08
Share the link
Someone else opens the same view
Drawing a line chart is a solved problem. Drawing one that stays honest when its values span several orders of magnitude, stays fast across years of readings, and can be reproduced exactly by someone who was sent a link, is not.
Analysts do not work alone. They compare series, find something odd, and send it to a colleague. A dashboard whose state lives only in memory looks completely fine to the person who built it and fails the moment it is shared. 'Here is the link, now set the filters like this' is not a feature.
And every screen wants more series, more rows and more history than a browser renders comfortably. Every honest answer to that involves a trade-off (summarising, virtualising, choosing a scale), and the reader has to be able to see which one was made.
How do you keep a dense dashboard fast and honest at the same time, when every honest answer involves a trade-off the reader should be able to see?
How is the data drawn?
What is the dashboard actually showing?
What can an analyst take away?
How does it reach people?
The data team owned collection, storage, summarisation, and the server-side aggregation behind the charts. The design team owned the visual direction. Frontend ownership covered the dashboard requirements, chart layer, filter model, tables, exports, and delivery.
A single chart can hold a site drawing megawatts alongside one drawing kilowatts. A linear scale flattens the small series into the axis; a logarithmic one misleads anyone who does not notice it.
Scale became an explicit, labelled control rather than something the chart decided quietly on the reader's behalf.
The same numbers appear in three places. Any of them re-deriving or re-fetching independently is a disagreement waiting to be discovered by the analyst rather than by us.
All three are derived from one prepared dataset, and exports are generated from what is already on screen.
An export that needs editing before it can go in a report is an export nobody uses. It has to carry what it was filtered to, and match the view it came from.
The active filters and scale are stamped onto every export, and image and PDF output is produced from the live page.
The dashboards were designed for a large screen and are routinely opened on a 13-inch laptop in a meeting.
Layouts reflow rather than scale down, and dense charts stay legible at the width they are actually read at.
The application is built and served as static assets. There is no server-side rendering step and no backend of our own to move slow work into.
Anything expensive is either bounded before it arrives, deferred until it is needed, or cached. There is nowhere else to put it.
Filters live in the page address rather than in memory, so any view can be shared as a link and survives a reload. Everything downstream (the data request, the charts, the tables and the exports) is derived from that one source, which is why they cannot disagree with each other.
Analyst
Comparing, drilling in, exporting
Dashboard
Static build on S3 + CloudFront
Page address
FiltersDate rangeScale
Filter layer
Parses and validates what the URL asks for
Data request
Cached, deduplicated, never sent twice
Shaping
Server-side summarisingScale applied
Outputs
ChartsTablesExports
Each of these solved a real problem and cost something in return. The trade-off is stated alongside the decision, because a decision without one was not really a decision.
Decision 01
Problem
Decision
Trade-off
Result
Decision 02
Problem
Decision
Trade-off
Result
Decision 03
Problem
Decision
Trade-off
Result
Decision 04
Problem
Decision
Trade-off
Result
A chart is an argument about data, and the reader cannot check it unless they can see which choices were made. In energy data those choices are unavoidable: the values genuinely span orders of magnitude, and something has to give.
The temptation is to pick a scale automatically and say nothing. It produces a cleaner interface and a chart that can silently change its own meaning between one filter and the next.
One question organised the whole application: what is allowed to know what the dashboard is currently showing? The answer is exactly one thing, and everything else derives from it.
Filters, date range, chart scale
The source of truth. Everything downstream reads it, which is what makes a view shareable and the back button correct.
Summarised series and table pages
Keyed on the parsed filter state, so identical views are never fetched twice and returning to one is instant.
Prepared chart data
Computed once per filter change and reused by charts, tables and exports, which is the reason the three cannot disagree.
Hover, tooltips, column visibility
Deliberately local and deliberately not in the URL. A shared link should reproduce a view, not someone's cursor.
Exports are where dashboards quietly lie. The export re-fetches or re-derives its data and ends up disagreeing with the view the analyst was looking at, and nobody finds out until it is already in a report.
Filters, Date range, Chart scale all resolve into The page address (Parsed and validated once, then used as the cache key), which is then read by Charts + tables and Exports.
Putting the dashboard in the URL means every filter change is a parse-and-validate step, and the format becomes a compatibility surface the moment someone shares a link.
ChoiceTreat the address as the source of truth anyway. Shareable, reloadable, back-button-correct views were worth the machinery, though I should have versioned the format from the first commit rather than the first breaking change.
A silent automatic scale is simpler to build and puts one less control on screen.
ChoiceMake the scale explicit and labelled. More state to manage and one more thing on screen, but a chart that can silently change its own meaning is not simpler for the person reading it.
SVG is accessible, inspectable and easy to style. Past a certain number of marks it is also the slower answer, and canvas is not.
ChoiceStay on SVG and bound the point count by summarising server-side. It held, but I leaned on summarising longer than ideal to avoid giving up what SVG made easy.
Analysts can move through a large dataset without waiting on it, share an exact view as a link, and export something faithful enough to drop straight into a report.
Internally, the more useful outcome is that charts, tables and exports became three views of one state. New visualisations inherit filtering, scaling, caching and export instead of reimplementing them.
Analysts collaborate by sending links. Any state model that cannot survive being pasted into a chat window is solving a different problem from the one the product has.
Automatic scale selection removes a control and adds an unanswerable question. The reader cannot check an argument whose assumptions are hidden.
The instinct is to throttle everything. Charts struggle with points drawn and tables with rows in the page. One problem, two fixes, and throttling both would have solved neither well.
The URL filter format became a compatibility surface the moment someone shared a link. Retrofitting that after the first breaking change was avoidable work.
05 / Contact
Available for senior frontend, product engineering, and AI product engineering opportunities.
Location
Bangalore, India · Remote