Data EngineeringAI AgentsAnalyticsClaude

We stopped designing dashboards

How I found a dashboard in our Metabase that nobody on the data team had built — and why that's the win we'd been working toward without realizing it.

Giuseppe Intilla
7 min read
Abstract teal and amber blog artwork showing an analytics dashboard, chart cards, and a flowing data layer.

We were in Algarve for our team retreat. Sometime between a session on AI roadmap and dinner, Luis — one of our AI engineers — asked me for the Metabase API key. He was working on Dynamic Messages, the layer of the product that decides what we say to a shopper at any given moment, and he wanted to monitor how the new version was behaving. I sent him the key, went back to whatever I was doing, and forgot about it.

A few days later I opened Metabase for an unrelated reason. There was a new dashboard there. I hadn't built it. Nobody on the data team had built it. Luis had built it, on his laptop, in the gaps between retreat sessions, using Claude Code and the Metabase API.

It was a real dashboard. Not a sketch. The right charts, the right filters, the right joins. The kind of thing that, 4 years ago, would have been a ticket in backlog, a meeting to scope it, a small slice of someone's sprint to build it, a back-and-forth on which metric we actually meant.

That was the moment I realized: we had stopped designing dashboards. I just hadn't noticed yet.

The dashboard was the easy part

For most of the time I've worked in data, designing a dashboard wasn't really design. It was archaeology.

You took the request — "I want to see how the new flow is performing" — and the actual work was figuring out which table held the data, at which grain, with which definition of "performing", and what to do about the three different sources that all claimed to know when a message was sent. The chart at the end of that process was the easy part. The bottom of the iceberg was knowing the data well enough to answer the question correctly.

That knowledge lived in people. It lived in me, in the data team, in whoever had been around long enough to remember why a column was named the way it was, which staging model was load-bearing and which was a relic, which join would silently double-count, which timestamp was real and which one was the timestamp of the webhook arriving and not the event happening.

For a long time, that asymmetry was the data team's actual job. Building the dashboard was the deliverable, but the work was the archaeology that made the dashboard correct. The chart was the receipt.

What changed

A few things changed at once.

Agents got good at writing SQL. Claude can read a schema, pick the right table, write a query that joins correctly, and iterate when the result looks wrong. Not perfectly — but well enough that a non-data engineer with a goal can get to a working query in minutes.

Agents also got good at driving APIs. Metabase has an API. Building a dashboard programmatically used to be tedious enough that nobody did it; now an agent does it in one shot. Card, dashboard, filters, layout. Same for Looker, same for whatever else.

But the part that actually mattered was less visible: our marts got good enough to be read by someone who didn't know us.

When Luis sat down at his laptop in Algarve and pointed Claude Code at our warehouse, he didn't need archaeology. The right tables were obvious from their names. The right columns were obvious from their descriptions. The grain was already the grain he wanted. The hard part of the job had been done, months earlier, by people whose names aren't on the dashboard he built.

That's the part I want to be honest about. The dashboard appeared in twenty minutes because the data layer had been getting ready for it for a year.

The bottleneck moved

Once you notice this once, you start seeing it everywhere.

A PM wants to compare campaign performance across orgs. Used to be a dashboard request. Now it's an afternoon with Claude Code, a working chart, and a Slack message asking if the join looks right.

A CS lead wants to spot churning accounts. Used to be a dashboard request. Now they describe what they mean by "churning" to an agent and iterate on the query until the list matches the accounts they already knew about.

In each case, the data team is still in the loop — just not as the builder. We get pulled in when something looks off, when a number doesn't match what someone expects, when the agent confidently picked the wrong column. We're the second opinion, not the first draft.

That's a real change. The bottleneck used to be "can a human build this dashboard." Now the bottleneck is "is the data layer legible enough for a non-expert to ask the right question." When the answer is yes, the dashboard builds itself. When the answer is no, it doesn't matter how good the agent is — the chart will be wrong in a way nobody catches until it's too late.

What the data team actually does now

This has changed what good data work looks like, day to day.

We design marts that anticipate questions, not dashboards that answer one. The unit of work used to be a chart. Now it's a table that's good enough to support a hundred different charts we'll never see. That's harder to demo and easier to undervalue. It's also where the leverage is.

We write column descriptions like we're writing prompts — because we are. The audience for a schema.yml description used to be a future analyst, maybe. Now it's an agent, every single time someone runs a query. A vague description gets you a wrong join. A sharp one gets you a correct answer. The descriptions that used to be optional documentation are now load-bearing infrastructure.

# Before — written for humans who already know the system
- name: sent_at
  description: When the message was sent

# After — written for an agent that has never seen this table
- name: sent_at
  description: >
    Timestamp the message was handed to the channel provider
    (WhatsApp, SMS). Use this for send-volume and throughput metrics.
    NOT delivery confirmation — see delivered_at.
    NOT the webhook receipt time — see event_received_at in
    stg_message_events.

The second version is longer than anything we'd have written a year ago. It's also the one that gets the join right on the first try, without anyone from the data team in the loop.

We push back on column names that require tribal knowledge. The naming bar moved up. If a column's name needs me in the room to make sense of it, that's a bug, not a style preference. The agent sees the name before it sees anything else.

Old nameNew nameWhy
statusmessage_send_statusstatus alone is ambiguous across marts
typemessage_channelwhatsapp | sms | email, not a "type"
created_atmessage_created_atwhich row's created_at?
org_idorganization_idsame key, same name, every mart

None of these renames shipped a feature. Each one removed a question an agent would otherwise have had to guess the answer to.

We kill staging models that look like marts. The biggest risk in this new setup is an agent picking up a half-cooked intermediate table because the name didn't make its role clear. So we name them defensively, hide them when we can, and stop pretending that internal models are private just because nobody asked about them yet.

And we say no to dashboard requests in a way we didn't used to. Not "no, we won't build it." More like: "the data is there, the marts are clean, build it — and ping us if the numbers don't look right." That sounds like passing the buck. It isn't. It's an acknowledgment that the work we'd be doing if we built it ourselves isn't the dashboard. It's everything underneath.

The work didn't disappear, it just moved

If your data team still measures itself in dashboards shipped, you're measuring the wrong thing.

The dashboard was never the product. It was the cheapest interface we had between people and the warehouse. Cheap interfaces get replaced. That's not a threat to the data team — it's a sign that the layer underneath finally got good enough to be used without us in the room.

What doesn't get replaced is that layer: marts that mean what they say, columns named like they'll be read by someone who's never seen them before, descriptions that disambiguate instead of describe. That work used to be a means to the dashboard. Now it's the whole thing.

The dashboards still get built. They just don't get built by us anymore. And that's the win — not because we wanted to stop, but because it means the data finally got good enough that we didn't have to.