Why Merlin changed from single-tenant to multi-tenant
An architecture decision that felt final turned out to rest on a business assumption. Once the assumption changed, the decision deserved a second look.
Written
An architecture decision that felt final turned out to rest on a business assumption. Once the assumption changed, the decision deserved a second look.
There's a note from the planning session for the first transaction system we ever scoped. It read, simply: "No need for multi-tenant."
That decision was locked in. And at the time, it made complete sense.
A few months later, before a single line of code was written, we reopened that decision.
A decision as a consequence of its premise
Our business model at the time was clear: build a demo to pitch, tune it to the client's needs once the deal closed, then deploy it. One project per client.
Within that model, a single-tenant architecture was a direct consequence of the premise. One deployment per client, each instance standing on its own. Clean, controlled, matching how we already worked.
We knew there was another pattern. Our earlier reference work had already used multi-tenant. But for this context, we deliberately chose single-tenant. The decision was locked in.
An assumption that shifted
What changed it was our understanding of who this product was actually being built for.
From one conversation with business owners to the next — from a friend running a fashion brand to several vape store owners — the same pattern of problems kept surfacing from different people. Scattered operations, disconnected data, accountability lost between shifts and branches.
Slowly, what we were planning stopped being something for one incoming client and became something meant to answer a broader pattern of problems. The scale was different.
A decision that changed with it
Once the assumption changed, the decision that had been locked in became an open question again.
A per-client model at this scale meant every new user was a new deployment, maintenance growing linearly. The premise behind the decision had changed, and the decision had to be revisited with it.
Merlin ended up built with a multi-tenant architecture from the very first migration. The decision changed before the first line of code was written.
A consequence built along with it
A shared-database multi-tenant setup means isolation between shops becomes entirely the code's responsibility. A few months after Merlin went live, we found a bug in the rate limiter: every tenant shared the same bucket because internal traffic took a different path than expected. One busy shop could make other shops start getting error responses.
An architecture decision keeps living inside every system built on top of it.
A decision as a hypothesis
The initial decision we locked in for one demo project was the best hypothesis given what we understood at the time: for one client, a per-project model, limited scale. In that context, the decision was valid.
What changed was our understanding of who this product was being built for, and how broad the problem pattern we wanted to solve actually was. Once that understanding changed, a decision that felt final had room to be revisited.
It changed because our understanding of the product grew — not because the technology changed.
Category: Think
More entries
Why we rebuilt a variant grid already running in production
Merlin's admin variant grid had passed development and was running in production, until one complaint from real use got it completely rebuilt within hours.
Why Merlin's storefront was built with Astro
An online store is only useful if people can find it. That constraint decided the tool I used to build Merlin's storefront.
Why we left Laravel for Go
An overloaded server is what made us start looking for an alternative. But what kept us with Go ever since turned out to have nothing to do with that.