1. Six directories. Six copies. One truth repeated 2,112 times.
I was looking through a repository one afternoon when something made me uneasy. Each tenant directory had its own index.html. Opening them, I found 370 lines each. Almost identical. The parts that actually differed between tenants — app name, theme color, slug — added up to 18 lines. The remaining 352 lines were exact copies.
With six tenants, that's 2,220 lines of HTML total. 108 lines of meaningful difference. 2,112 lines of duplication.
Nobody had planned this. Features got added and copy-pasted. Urgent fixes got applied to each file by hand. The copies accumulated. The structure had quietly grown into something nobody would have designed on purpose.
2. "Works fine for now" and the debt it hides
The problem isn't visible while everything is working. With two or three tenants, applying the same change in multiple places feels manageable. There are moments where "just make it work" is the right call — and in those moments, this kind of structure can feel like a reasonable trade-off.
The problem surfaces when something needs to change. A UI bug. A design update. A new feature. Suddenly every change comes with a hidden tax: do this for all tenants. With six, that's annoying. With sixty, it becomes a process. With six hundred, someone realizes they've spent months managing copies instead of building software.
DRY violations usually start as small discomforts — that faint sense of "I'm doing this again." Ignore that feeling long enough, and the codebase accumulates quiet debt. By the time it's undeniable, the cost of fixing it exceeds the cost of building it in the first place.
3. The question you ask determines the solution you find
"How do we propagate changes to all tenants automatically?" and "Why does the same thing exist in multiple places?" look like they're addressing the same problem. They lead to completely different answers.
The first question optimizes the management of duplication. Build a sync script. Set up CI to update all tenant files in bulk. This looks like a solution, but it's actually a justification — it accepts duplication as the given and works around it.
The second question attacks the duplication itself. "The tenants only differ in 18 lines — can I keep just those 18 lines in the tenant file and put everything else in one place?" That question changes the design from the root.
The quality of your question determines the quality of your answer.
Ask "why is this the way it is?" before you ask "how do we fix it?"
4. The constraint that was breaking the design
So why wasn't it 28 lines from the start? There was a real constraint. For multiple PWAs to be installable as separate home screen apps, each one needs a distinct URL scope. Put them under the same path, and the operating system treats them as the same app. The multi-tenant PWA requirement was genuine.
The initial response to that constraint was "give each tenant its own repository." Separate repositories mean separate GitHub Pages deployments mean fully independent URLs. The multi-PWA requirement is satisfied.
But that decision carried a hidden assumption: separate repository means separate files. That assumption generated the HTML copies. The copies generated the duplication. The duplication generated the maintenance cost. A solution to one constraint had created a new problem elsewhere.
5. The Service Worker as a structural answer
The way out was the browser's Service Worker. In a PWA, navigation requests — accessing a URL — can be intercepted by a Service Worker. When someone accesses a tenant's URL, the Service Worker can return HTML from somewhere else entirely.
This means a tenant's index.html only needs to exist for one purpose: establishing the PWA scope. The actual UI — the DOM, the JavaScript — can be loaded from a single source. The only information that belongs in a tenant file is what only that tenant knows.
The single source of truth for UI:/hello-briefing/index.html
The single source of truth for logic:/hello-briefing/app.js
What a tenant file should contain: not everything else, but only what only it knows.
The Service Worker became four lines: a shell that says "load the real sw.js from the main location." When there's a bug, you fix one file. When there are 100 tenants, you still fix one file.
6. DRY is about knowledge, not code
DRY stands for "Don't Repeat Yourself," but the real definition from the source material is more precise: "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."
"The UI lives here." "The logic lives here." "This tenant's app name is this." Each piece of knowledge has exactly one home in the system. That's what DRY is aiming at.
Code duplication is a symptom, not the disease. The disease is knowledge scattered across multiple locations. Mechanically eliminating copies without asking where each piece of knowledge belongs just moves the problem around. The question "where is the authoritative home for this knowledge?" has to come first.
7. Trust the feeling of "this is ugly"
At one point in the conversation, someone said: "Scanning all tenants and patching them feels ugly." That instinct matters.
"I'm doing this again." "This feels roundabout." "Wait, I have to apply this change everywhere?" — these reactions are signals that the design is missing something. You can dismiss them as "unavoidable" or you can ask why they're appearing. That choice is often where the quality of a design gets decided.
With experience, this "ugly" detector gets more precise. It's not about aesthetic preference. It's a nose for knowledge duplication. Trust it.
8. The question is never finished
By the end of the day, a 370-line file had become 28 lines. A Service Worker was four lines. With a hundred tenants, changing the UI would still mean touching one file. There's a name for that state: a single source of truth. Changes collect in one place and propagate outward from there.
But none of this was visible from the start. "It works." "We're in a hurry." Those decisions accumulated into duplication. The question "why does the same thing exist in two places?" is what dismantled it.
DRY isn't a rule. It's a question. Perfect DRY doesn't exist, and not every duplication needs to be eliminated. But keeping the habit of asking — "why is this here, in two places?" — is what slowly makes a design honest.
DRY isn't about writing less code. It's about refusing to stop asking why the same thing exists in two places.
TokiStorage is building digital infrastructure to preserve voice, memory, and record for 1,000 years — with the mission of democratizing proof of existence. We try to let the design philosophy live inside the product.
Explore TokiStorage Read all essays