A media buyer running on our platform asked me, two weeks after we launched, why our data was three hours behind Shopify and Triple Whale.
I told him three hours was enough.
I said it with the confidence of someone who'd run paid media accounts for a decade. I had the credentials. I knew the rhythm of decision-making in those accounts. Three hours, six hours, a day — the structural decisions you make on the data don't move on a window that tight. The math, as I understood it, was on my side.
It took me a few weeks to realize that the technically correct answer was the wrong answer, and that the right answer was something I had to learn by going back to my own habits.
This is the story of that few weeks.
The bet behind Bratrax
We launched two weeks ago. The bet underneath the launch is simple: attribution is a property of the sale.
We don't want clients opening our dashboard and seeing more sales there than they see in Shopify. We want every Shopify sale broken down by the channel that actually produced it Google Ads, Meta, subscription, referral, organic, native. And we want the breakdown to clean-reconcile against Shopify's own count.
That sounds like a normal promise from an attribution tool, however after years of use of our competitors’ tools, we seem to be unique in this.
The purpose of our pixel is to enrich Shopify data, not vice versa. We literally use anything that is available to attach more information about marketing to any given Shopify sale.
We don’t have the scientific, non-scienttfic, the astrology one - whatever the latest cool name for an attribution model you can think of.
Our promise is the boring one: we match Shopify. Then we show you how each sale happened.
The second bet, deeper than the first, is the one this essay is about: every client is a resource to us, not the other way around. Every edge case a client surfaces makes the product better for the next one. We onboard clients as collaborators, not as revenue. The data they let us see, the bugs they catch, the questions they ask those compound into something the next client benefits from on day one. We do too.
The numbers so far suggest the bet is working. Clients who arrive with 80% of their sales sitting in the "direct" bucket (meaning we can't attribute the sale to any channel), get that number down to 5–10% with Bratrax. That's not a marketing claim; it's just what happens when you trace every edge case to the ground.
Which brings me to one client in particular.
The client who took us past the bugs
In our first two weeks, we onboarded a handful of clients. Most of them surfaced the kind of feedback you'd expect — a column definition wrong, a campaign missing from a join, a date filter behaving weirdly. We fixed those quickly.
One client was different. He was running Bratrax next to Triple Whale next to his Shopify admin, comparing all three. He caught the same early bugs everyone else caught, but he kept going. Once the surface stuff was clean, once the columns rendered and the joins worked, he surfaced the architectural one:
The other major thing is the update interval, I mentioned before. This morning it was very apparent, I check BRATRAX at around 10:45am and there were no attributions or stats whatsoever, which is almost 11hrs into the day with nothing.
I gave up on looking at BRATRAX and was using TW. Then when I checked in the last hour or two it finally populated.
I'll leave the decision in your hands, but personally from my end, not being able to rely on it when I need it, combined with the delay in stats updating, are real concerns for me.
He was right. Depending on the time of day and how loaded our pipeline was, Bratrax was running anywhere from one to six hours behind reality. He could see a sale in Shopify, see it in Triple Whale, and not see it in Bratrax. He'd refresh. He'd refresh again. Nothing.
My first reaction was defensive. I'm a former media buyer myself — that's my background, and it's the background of most of our early users. I knew the argument. I had the muscle memory for it. Structural decisions don't move on a one-hour timeline. You don't pause a campaign based on data from forty minutes ago. You don't reallocate budget based on a single new conversion. The decisions that matter happen on rhythms of days, not hours.
So I told myself the equivalent of: he's wrong about the impact. The product is fine.
I held that line for longer than I'm proud of.
A technical aside — what was actually happening
For the technical readers. Skip to the next section if this isn't the part you came for; you won't lose the thread.
Bratrax stores everything in ClickHouse. Raw ingestion from the ad platforms, the Shopify webhook stream, the browser pixel events — all of it lands in ClickHouse. The flattening from raw JSON into typed columns happens in ClickHouse. The cross-stream joins, the dimension tables, the attribution math, the materialized views — all in ClickHouse. ClickHouse is both the cold storage and the computation engine.
What ClickHouse isn't historically optimized for is the sub-millisecond aggregation queries a dashboard fires every time the user pans a date range or filters a chart. For that pattern, DuckDB is the standard answer — local, embedded, columnar, optimized for the read pattern of a single analyst hitting a single dataset over and over.
So the original architecture was: keep everything in ClickHouse for storage and heavy compute, and run a periodic load from ClickHouse into DuckDB for the dashboard layer. The user hits DuckDB. DuckDB queries return in milliseconds. The same query against ClickHouse over a network hop can be 50 to 200 times slower. The cache made the dashboard feel instant.
The problem was the cache's freshness. Keeping DuckDB current required a periodic load — hourly — from ClickHouse. Under concurrent load across multiple clients, those loads queued. The API refresh from the ad platforms ran every three hours. The DuckDB load was supposed to take minutes. Sometimes it took longer. A three-hour upstream refresh, plus a queued downstream load, plus the natural slack between them, produced moments where the dashboard was looking at data six hours stale.
We had built a fast layer to insulate users from slow storage. The fast layer became the slow part.
The fix, when we got there, was to remove DuckDB entirely. We pointed our dashboard layer to query ClickHouse directly. Per-query, the dashboard is slower than it used to be, but the data is always current. We also changed the ETL pulls from the ad platforms and Shopify to land data sooner. So we moved from 3 hour window to 1 hour window for Shopify and adnetwork pulls.
That's the mechanics. The reason we made the change is the rest of this essay.

The question I kept asking
While I was sitting with the feedback, the question I kept asking myself was the one I led with: do you really make better decisions if you have data faster?
I asked it honestly. As a media buyer, the honest answer is mostly no. Below an hour, you're not making structural decisions on the new data. You might surf budgets a little at high scale, the cynical part of me would say "ain’t no BFCM weekend every day". But on big picture your bottleneck in regards to time, will always be that ad networks take time to learn who your audience is and how your creatives/strategy can fit into that. So literally our decision making is always constrained by how long ad networks take to learn your business.
Secondly the attribution signal a third-party platform like ours reports is downstream of what the ad platform itself sees. Meta has its own attribution. Google has its own attribution. They see the click, they see the conversion event, they make their attribution decision. We see the same conversion event later, we apply our own attribution logic to it, and our number gets published with a lag relative to theirs. You're never going to beat the platform that owns the signal. So even with instant data, the operationally useful signal is still on Meta's clock, not ours. (I know that someone will bring up offline uploads/CAPI but that’s another thing that is still functioning on the level of hours, not minutes)
So I kept telling myself: three hours is fine. Six hours is sloppy, but it's not breaking anything important. Faster is a nice-to-have, not a must.
This is where I stayed for too long.
The thing I missed
Then I caught myself doing something I used to do every day as a media buyer.
Hitting refresh. Watching the dashboard. Refreshing again. Then once more, even though I knew nothing had changed. Then a fourth time, watching for a number to tick up.
I used to tell my team at Inceptly that this behavior was a waste of time. And on a decision-making basis, it was. It is. Nothing I saw on a five-minute refresh was going to change the campaign decision I'd already made for the day.
But that's not what the behavior was for.
It was a ritual. A way of being present in the account. Staring at the wall. Forcing my attention to remain locked into the problem so that, somewhere in the back of my mind, a solution could surface. The refresh wasn't the point. The refresh was the thing I did with my hands while my brain stayed in the work.
Different professions have different rituals for getting into and staying in their zone. Writers have theirs. Surgeons have theirs. Traders have theirs. For media buyers, obsessive refreshing of the account is one of them. It's not productive on its own. It's a way of being mentally available for the results to happen.
And when the data isn't fresh, that ritual collapses. The operator's attention gets pulled out of the work and into a different question. Instead of "how do I get this CPA down" or "what creative could I test next," the question becomes: wait — is this data even right? Why is this number missing? Is the pipeline broken? Should I be looking at Shopify instead?
That shift is the actual cost. Not the lost decision. The lost zone.
Our product wasn't slow.
Our product was breaking the flow state of the people who were paying us to use it.
That's the realization I was three weeks late to. The technically correct argument — that three hours doesn't change the decision — was answering the wrong question. The right question wasn't about decision quality. It was about whether our product helped or hurt the operator's ability to do the part of their job that actually mattered: stay present in the account long enough for a good idea to show up.
We were hurting it.
What we built instead
We ripped out DuckDB. Bratrax now queries ClickHouse directly. Per-query performance is worse on paper — the dashboard renders in a hundred milliseconds instead of in tens of milliseconds. The lived experience is dramatically better, because the numbers are always current. The version of the dashboard the media buyer is staring at is the same version Shopify and the ad platforms are looking at, give or take a minute it takes data to land.
We also rewrote the ETL layer. The pulls from the ad platforms now run on a tighter window, so the upstream freshness improved at the same time as we eliminated the downstream cache lag.
The choice we made, stated plainly, is this: we chose presence over peak speed. Our product is no longer optimized for the answer "how fast can a single dashboard query return." It's optimized for the question "is the media buyer looking at it able to stay in the work."
That tradeoff would have looked wrong on a benchmark. It was the right one for the customer.

The lesson
The methodology I described early in this essay — every client is a resource, not a cost — is the kind of line that's easy to say and harder to live. The test of whether you actually believe it isn't how you treat the first piece of feedback. It's how you treat the piece of feedback you initially want to argue with.
The client who pushed us was the resource the methodology promised. We were defensive at first. We held the line for too long. We got there in the end.
The feedback you want to argue with reveals what you don't understand yet about the people you're building for. The argument feels right because, on the terms you currently understand the problem on, it is right. The thing you're missing is that the terms you understand the problem on aren't the only terms.
This is the part of the lesson that generalizes past attribution. You don't get to choose which feedback is the right feedback. You get to choose whether you sit with it long enough to hear what it's actually saying.
We're going to make this mistake again. With different customers, on different parts of the product, in different defensive postures. The bet is that we sit with it. The bet is that the next client who points at something we want to argue with is a resource we get to learn from, if we let them be.
If you've been burned
If you've been burned by attribution tools that show numbers that don't match your Shopify export, or that hand you data from six hours ago when your account is on fire right now — try Bratrax.
We're $79 a month for the first 100 Founding Members. Self-serve signup. Dashboards live in 60 minutes.
We built it on the bet that the feedback you want to argue with is the feedback that makes the product real. Bring us your edge cases.
Try it here: https://bratrax.com/signup
— Brat
