Any vendor writing this post has an answer before they start, so here is ours up front: sometimes you should build it, and we would rather say which cases than pretend there are none. A pitch that claims buying is always right is a pitch nobody senior believes.
The build case for BI has also got genuinely stronger. DuckDB, Parquet, Arrow and a competent model make a semantic layer and a dashboard renderer a plausible quarter's work for a good team. That is new, and it deserves a straight answer rather than a reflex.
Three cases where building is right
| The case | Why buying fails | What you are actually building |
|---|---|---|
| Analytics is the product | You are embedding dashboards in software you sell. The look, the URL structure and the per-tenant isolation are product decisions, and a vendor’s roadmap is not yours. | A rendering layer over your own model. Narrow, and you own the requirements. |
| One question, enormous scale | A single high-volume view — one fact table, one grain, one audience. A general BI tool is most of its cost in flexibility you will not use. | A purpose-built page. Often a week, and correctly so. |
| A regulator owns your architecture | Constraints that no vendor will meet on your timeline: an air gap, a specific jurisdiction, code review of everything that touches the data. | Whatever the constraint dictates. The constraint is the requirement. |
Notice what is not on that list: cost. “We can build it cheaper” is almost always an estimate of version one against a mature product, which is not the comparison you will live with.
xychart-beta horizontal
title "Weeks over the estimate, by component"
x-axis ["Charts", "Model", "Security", "Refresh", "Audit", "Migrate"]
y-axis "Weeks over estimate" 0 --> 12
bar [1, 10, 9, 4, 5, 9]
The question that actually decides it
Not “can we build this?” — you probably can. It is “who owns it in year two, when the person who built it has changed team?”
This is the failure mode nobody prices in, and it is specific rather than vague. An internal BI layer built by one strong engineer recreates the key-person dependency it was meant to remove — one level down, and now in code nobody else has read.
flowchart LR
A["Year 1
one engineer, fast progress,
everyone delighted"] --> B["Year 2
they move to a
higher-profile team"]
B --> C["Year 3
nobody will touch it"]
C --> D["Year 4
a migration project
nobody planned"]
style A fill:#e6f7f2,stroke:#9fd8c9
style C fill:#fff4e6,stroke:#f0cb9a
style D fill:#fdecec,stroke:#eab6b6
Ask it concretely before you commit: name the two people who will own this in eighteen months. If you cannot, or if the honest answer is “whoever is free”, that is the decision. Not a preference — the decision.
The incident in row three
Nine months in, a panel’s cached result was keyed on the query text alone. Two users with different row entitlements shared a cache entry, and one of them saw the other’s region for about forty minutes before someone noticed. Nothing was malicious and nothing left the building — but it was reportable, and the four weeks of rework was the cheapest part of it.
Version one was on time. Version three — with security that holds everywhere, scheduling that recovers, provenance a compliance team accepts, and a way off the old tool — was four times the estimate. Version three is what you are comparing against a product.
What people underestimate in a BI build
The rendering is the fun part and it is not the work. Four things consume a build, and three of them are invisible from the outside.
| The work | Why it is underestimated | Rough weight |
|---|---|---|
| Charts and layout | Highly visible, genuinely tractable with a grammar-of-graphics library. | ~15% |
| The semantic layer | “Just SQL generation” until joins, grain, time grains, level-of-detail and derived fields arrive. Then it is a compiler. | ~30% |
| Row and column security | Easy to demo, hard to guarantee. It must hold on every path — dashboard, export, API, AI answer — and the cached-result key must include it. | ~25% |
| Operations | Refresh scheduling with backoff, extract versioning and rollback, audit, provenance, PDF rendering, a migration path off whatever you have now. | ~30% |
The one that catches everyone
Caching and security are the same problem, and most builds discover this after go-live. If a panel’s result is cached on the query alone, two people with different row access share a cache entry and one of them sees the other’s numbers. The key has to include the policy — and the cleanest way to guarantee that is for the policy to already be inside the SQL the key is computed from.
That is a design decision you make once, early, and it is very unpleasant to retrofit.
The honest comparison
Every feature is yours to prioritise, nothing is licensed per seat, and the thing fits your organisation exactly. You also own security holes, migration tooling, refresh reliability, and the staffing question above, permanently.
The vocabulary, the dashboards and the data stay yours — in an open format, in your own cloud. What you do not own is the compiler, the policy engine, the importer and the operational surface. That is the trade, stated plainly.
The middle path most estates should take
The interesting option is rarely build-everything or buy-everything. It is: buy the governed layer, build the two or three things that are genuinely yours.
- The definitions are yours either way. A model is a readable, versioned document. That is the asset, and no product should hold it hostage.
- The data is yours either way. Parquet in your own bucket. If you removed the product, every file stays readable.
- Build the embedded surface if analytics is your product. Query through the API, render it yourself, keep your own look and URL structure.
- Do not build the compiler or the policy engine. These are where the subtle,
expensive, security-relevant bugs live, and they are entirely undifferentiated. Nobody
has ever won a customer because of their own
GROUP BYgenerator.

If you have already built it
Most organisations reading this have something — a Streamlit app, a set of scheduled notebooks, a dbt layer with a thin viewer on top. That is not wasted, and it is usually the most valuable input available.
The dbt models are your definitions, already argued over. The scheduled queries tell you the real refresh cadences. The notebook nobody can retire tells you which questions actually matter. Bring all three to the evaluation rather than starting from a blank requirements document.
The test to run before you decide
- Write down the two owners for year two. If you cannot name them, buy.
- Ask your security lead to describe how row security survives caching. If the build team has not thought about it, that is the size of the gap.
- Count the dashboards you would have to migrate. Build gives you no importer. Every one of them is rebuilt by hand, by someone, on a deadline.
- Price version three, not version one. Version one is a quarter. Version three — with scheduling, audit, export, provenance and migration — is the product you are actually comparing against.
If you buy: the first ninety days → · How to tell whether it worked →