How a Team of One Built our Product Analytics Data Warehouse in Just One Week | Analytics Engineering in the Agentic Era
A team of one built our product analytics warehouse in a week. Here's how a coding agent, real guardrails, and the ETM loop made it safe.
Co-Founder & CEO

I was wrong. A few months ago I made a blog post saying how you shouldn’t vibecode your data warehouse. While the general point still stands, the implementation details matter so much more. One person and a coding agent, with proper skills and guardrails, can build a data warehouse from scratch. In just a week. I am living proof of that.
The problem and temptation
Exmergo Viz, our Dashboard AI Agent product, produces a lot of operational exhaust. Every time someone creates a dashboard, publishes one, uploads a file, or asks the agent a question, something happens in our production database. That database is Firestore, and the way its data reaches our warehouse is a change-data-capture export that lands in BigQuery.
If you have ever worked with a CDC feed, you know what that looks like on arrival. It is not a clean table you can query. It is an append-only stream of mutation events, one row per change, with the actual document body carried along as a JSON string. There is no dimensional model, no grain you can trust, no tested definitions of what “an active user” or “a published dashboard” even mean. It is raw material coming from a database optimized for scalable transactions, not an analytical warehouse. The business questions I actually wanted to answer, how many dashboards get created and published, how files get uploaded and used, how people engage with the agent, were all sitting in there somewhere, encoded in a shape nobody could query directly.
Turning that into a documented, tested, dimensional model is a real and raw analytics engineering project. Just under a year ago, you would staff the project with a 3–5 person team and measure progress in months (and in some cases: quarters). I had one person, myself, and I wanted it done in a week. As the CEO of an early-stage and fast-growth startup, that’s my responsibility.
I had argued that you should not vibecode your data warehouse, and I still believe the version of that argument that matters. I identified the risk: pointing a capable, fast, tireless agent at a production data source with no guardrails and just hoping that “it will just work”. But this is real production data. PII and mission-critical data. PII and security are non-negotiable constraints that shape every decision, not something we just bolt on as an afterthought. A naive agent let loose on raw customer data is exactly the nightmare my old post warned about.
So the real question was never “can an agent write dbt models.” Of course it can; just try it yourself. But what would it take for one person and an agent to build this safely, correctly, and cheaply? That question has an answer now, and the answer is the ETM loop (that’s right, ETM, not ETL).
The ETM loop: Explore, Transform, Maintain
Analytics engineering, when you strip it down, is three activities repeated forever. You make sense of what is in the warehouse. You turn it into models and metrics. You keep those models correct as the world underneath them changes. ETM: Explore, Transform, Maintain.
That loop is the backbone of Dex, the agent-native analytics engineering toolkit we built for exactly this. It is open source, Apache 2.0, and it structures the whole job into three phases, each one a command your coding agent can call.
Explore
Explore is how you make sense of the warehouse. It profiles columns, ranks the tables that actually matter instead of dumping a schema at you, infers how tables relate, and builds a cached map of the whole thing. It flags likely PII automatically as it goes. Crucially, it is read-only, and your data never leaves the warehouse.
Transform
Transform is how you author the dbt project and the semantic layer. Models, tests, documentation, metrics. Every change comes back as a reviewable diff, and any operation that would actually spend money against the warehouse gets its cost estimated and confirmed first.
Maintain
Maintain is how you keep the thing honest after you stop staring at it. It detects drift along several axes, schema, volume, grain, and semantic definitions, against a saved baseline, and proposes the corrective edits rather than silently overwriting your work.
Why does this loop matter so much for a solo build? Because it front-loads understanding and it never lets the agent run blind. The single most valuable thing that happened in my first hours on this project was that exploration caught load-bearing data problems before I had written a single model. Duplication in the raw feed, tables whose grain was not what anyone assumed, keys that were not actually unique. Those are the bugs that normally surface three weeks later as a failed build or, worse, a silently wrong number on a dashboard. Here they surfaced in the profiling pass, on day one, when they were cheap to fix.
The loop is a discipline, not magic. What makes it work is that a human stays in it at every phase. The agent explores, but I read the map. The agent proposes transforms, but I review the diffs. The agent detects drift, but I decide what to do about it.
The AI stack: Claude Code plus Dex
The agent is Claude Code. Dex is the skills layer on top of it that turns a very smart general agent into something you can actually trust against production data.
Cost governance came first. Every operation that touches the warehouse is cost-estimated, and nothing expensive runs without an explicit budget confirmation. On top of that there is a hard server-side cap on bytes billed per statement, as a backstop for anything that slips through. I set per-operation budgets up front and the agent worked inside them. I never once got a surprise bill, and I never had to babysit spend, because the tooling refused to guess with my money.
Exploration is read-only, and there is a query firewall. The agent can profile and probe, but it cannot exfiltrate values, and it refuses queries that would leak data out of columns flagged as sensitive. This is the mechanism that let me point an agent at real customer data and sleep at night.
PII detection is first class, and this is the part I care about most. Security and privacy are among our top priorities at Exmergo, and that stance is exactly why we built automatic PII detection and careful credentials handling into Dex rather than treating them as someone else’s problem. Dex flags likely PII as it explores. When it over-flags, and it will, because the honest truth is that automatic detection is conservative, you do not hand-edit some hidden state file to dismiss it. You record a durable, reviewable override with a written reason, and that decision lives in version control where a teammate can see it and disagree with it. A false positive becomes a small, auditable diff instead of a silent hack.
Credentials are keyless by default. No keys, no secrets committed to the repo. Authentication runs through application default credentials locally and Workload Identity Federation in CI. Secrets handling is a property of the design, not an afterthought. There is exactly one place in this whole project where I deliberately stepped outside that principle, a managed service that simply does not support keyless auth on the tier I was trialing, and even that exception is scoped down to a single read-only credential and documented as reversible. The point is not that we are dogmatic. The point is that every break from the secure default is a conscious, written decision, not an accident.
Everything is a reviewable diff with a human gate. The agent writes the SQL and the YAML, and the agent runs the commands. I review every diff, I approve every budget, and I am the only one who commits. The working rule on this project fit on one line: Marco commits, Claude codes.
Is any of this just vibes? No, and that matters to me. Dex is measured. On ADE-bench, the analytics engineering benchmark from dbt Labs, Dex paired with Claude Sonnet 5 leads the field at 76 percent, ahead of the bare agent baseline and well ahead of dbt’s own published agent skills, and it does it at lower cost than the alternatives. You can read the methodology and the full table at www.exmergo.com/benchmarks. I did not build this project on faith. I built it on a toolkit that had already proven it could do the work.
I will also be honest about the rough edges, because, you know, a founder telling you everything went perfectly is a founder selling you something. Over the week I hit real friction. Early on the tool once resolved the wrong warehouse connector when I ran it from the wrong directory. A cost estimate for one verification pass undershot the real spend. Relationship inference was close to useless on our source, because a CDC export gives dozens of tables that all share a generically named id column, so the tool found hundreds of candidate joins that were almost all spurious. None of these broke the build or leaked data. The guardrails held. I worked around each one, and because Dex is open source, I filed them upstream, where several got fixed inside the same week I was building.
The data stack: from Firestore to BigQuery to dbt to a semantic layer
Here is the shape of the pipeline. Firestore emits a CDC export into BigQuery, where it lands as raw changelog tables. dbt turns that into a layered model, staging, then intermediate, then a marts star schema of dimensions and facts. A MetricFlow semantic layer sits on top of the marts. dbt Cloud then serves that semantic layer over an API to whatever consumes it. Every step of that was built with Dex, and every step had at least one genuinely hard problem in it.
Deduplicating the changelog. The raw feed is a stream of mutation events, so the current state of any document is the latest event for it. Every staging model dedupes the changelog down to current state with a window function over the document key, ordered by event time. I learned quickly not to trust the platform’s own “latest” convenience views, which turned out to be unreliable in ways that would have quietly corrupted everything downstream. Explore caught that before I leaned on them.
The grain bug we shipped and caught. This is my favorite story from the week because it is exactly the kind of thing that bites you in production. The initial CDC backfill and the ongoing live stream labeled the very same logical document under two different database path spellings. To a naive dedup, that looked like two different records, so one real thing showed up twice, duplicated a key, and broke a deploy. The fix was to key on the database-agnostic part of the document path rather than the full name. I want to be clear that an agent did not save me from this by being clever; the loop saved me. The deploy failed loudly, the drift and profiling tooling pointed at the grain, and the fix went in as a reviewed diff. Fast, honest, recoverable.
JSON living inside strings. Because the document body arrives as a JSON string, BigQuery’s JSON functions become a minefield. Dynamic paths have to be compile-time literals, so you bracket-index the dynamic part instead. The function that lists an object’s keys recurses into nested objects unless you cap its depth. These are not glamorous, but they cost real debugging time, and they are the reason I keep insisting that exploring before modeling is not optional. You find the shape of the data before you commit code to a guess about it.
Delete-aware staging. In a CDC feed, a delete is just another event, and it often carries a null payload. If you filter naively, deleted records look alive forever, which quietly inflates every count you care about. So the staging models decide liveness from the latest event and keep an explicit deleted flag, and the marts choose per model whether to count deleted activity or exclude it. That is a semantics decision, not a cleanup, and it is documented as one.
PII handled in the model, not just the tool. Sensitive columns are tagged at the staging layer and kept out of the marts entirely. The clearest example is files. We track that a file was uploaded, and we derive counts from it, but we never scan the contents of a customer’s file and we never expose them. The security stance from before is not an abstraction here. It is a concrete rule about which columns are allowed to exist in the layers people actually query.
On top of the marts sits the semantic layer, defined in MetricFlow. Entities, dimensions, measures, and metrics, so that a metric is defined exactly once and can be queried without anyone hand-writing a join. That is where product questions finally become answerable: dashboards created and published, files uploaded, engagement, AI usage, all defined in one place with their caveats written down next to them. dbt Cloud then serves that semantic layer over an API. I want to be candid that this last piece is the one deliberate tradeoff I mentioned earlier. I ran it as a tightly scoped, reversible experiment on a trial, read-only, with no change to the keyless CI/CD pipeline that builds the actual data. dbt Cloud does not build anything here; it only reads and serves.
Wrapping all of it is governance that a single person can actually operate. Continuous integration builds every pull request into isolated datasets. Promotion is trunk-based: merging to main deploys to a QA environment automatically, and production sits behind a manual human gate. Every statement is cost-capped. The reason one person can run this without a team is that the guardrails are the system. I am not relying on a large group’s collective discipline to avoid a mistake. The tooling makes the safe path the default one.
Use Dex to build your data warehouse
Let’s close the loop on my earlier post. The advice “don’t vibecode your data warehouse” was right about the risk and wrong about the ceiling. If “vibecoding” means turning an agent loose with no cost controls, no PII enforcement, no reviewable diffs, and no human reading the output, then yeah, keep it far away from your production data. But that’s not the only option. With the Explore, Transform, Maintain discipline, real cost governance, PII and security as defaults rather than afterthoughts, and a human reviewing every diff, one person and an agent can ship a real warehouse, tested and governed, in a week. I know, because I did.
If I had to compress what made it work into a few lines: guardrails over autonomy. Explore before you model. Review the diffs. Approve the costs. Security by default, every time. None of that slows you down once the tooling enforces it. It’s what makes you go fast without breaking the important stuff.
Dex is open source under Apache 2.0. It works with any coding agent, including Claude Code, and it supports the major warehouses. If you have a raw data source and a warehouse and a stack of product questions you cannot answer yet, this is the fastest honest path I know from one to the other. Start at www.exmergo.com/dex, check the numbers for yourself at www.exmergo.com/benchmarks, and go build the warehouse your team keeps saying it does not have time for. Turns out, you might.
Install command: npx skills install exmergo/dex
