This started, as these things often do, because I was trying to delete some DNS code.
HomeLabRemote did not begin as a clean-sheet application. It evolved around functionality that already existed, with a SwiftUI application gradually wrapping operating processes, APIs, scripts and services that were already doing useful work.
That worked. Until I wanted to change one of them.
Refactoring the DNS model and then removing the obsolete implementation exposed how much knowledge of the old design had leaked across the application. Something that should have been a bounded removal required tracing assumptions through the UI, API routes, operational code, helpers and status signals.
The problem wasn't that the code couldn't be removed. The problem was that removing it required understanding too much unrelated code.
And, once again, I had apparently intuited my way toward ideas academics have been teaching for decades under names like separation of concerns, dependency inversion, information hiding, ports and adapters, and the Rule of Three.
Fine. They can keep the citations.
I needed a rule an operator could actually use.
The principle
Architecture follows demonstrated consumption, not anticipated reuse. A capability belongs to its sole consumer until another consumer actually needs it; at that point it becomes Core. When consumers disappear, ownership is recalculated: two-to-one returns the capability to the remaining consumer; one-to-zero removes it.
The rules are deliberately mechanical.
One consumer means custom. If exactly one application consumes a capability, that capability belongs to the application.
More than one consumer means Core. A capability becomes Core when a second application actually consumes it.
Core means shared, not important. Core is not a promotion in status, permanence or sophistication. It describes the number of consumers.
Do not abstract ahead of evidence. Build for the first consumer. Generalize when the second consumer proves that the abstraction exists.
Every application declares what it adds and what it consumes. At each architectural layer, its incremental implementation and its Core dependencies must be identifiable.
The rule applies independently at every layer. UI, application translation and production can each contain application-specific components and Core dependencies.
Applications own their internal features. If an application implements a capability, it owns that capability. It does not automatically own the distinctions that exist inside the implementation.
Anything exposed independently is its own boundary. An independently visible or manageable feature requires its own lifecycle contract.
The application owner owns feature extraction. Removing an internal feature should not require the platform's engineers to reverse-engineer the application's implementation.
Core has no entitlement to immortality. If a shared capability loses consumers, its classification changes with reality.
Removal happens in two sweeps
Removing an application should be boring.
Sweep one: remove the application. Delete everything owned exclusively by that application across its UI, translation and production layers. Do not start excavating Core.
Sweep two: recalculate Core. For every Core capability the removed application consumed, count the consumers that remain:
| Remaining consumers | Result |
|---|---|
n > 1 | It remains Core. |
n = 1 | It is no longer Core and becomes specific to the remaining application. |
n = 0 | Delete it. |
The architecture therefore follows the product in both directions. A second consumer promotes something into Core. Removing that consumer can demote it again.
The architecture
The functional boundary is equally simple:
UI Surface ↔ Application Translation ↔ Production
The UI Surface owns user input, presentation and interpretation of results into the appropriate UI signal.
The Application Translation Layer translates user intent into production operations and translates production results back into application meaning.
The Production Layer reads, calculates, mutates, executes and reports results.
Each layer knows the contract offered by its neighbor. It should not need to know how the layer beyond that contract works.
If production changes how it determines whether an agent is enrolled, the UI should not care. If the interface is replaced by a command line, production should not care. If either change requires rewriting the other end, the boundary isn't real yet.
The test
And this is where my version gets less academic.
I don't want an architect to tell me the system is modular. I want to test it.
Give an operator who understands the product but has never read the code an application's declared ownership and dependency map. Then tell them to remove the application.
They should be able to:
- identify everything declared as application-specific;
- hack it out;
- remove the application's declared consumption of Core;
- recalculate the affected Core consumer counts;
- delete
n = 0capabilities; - reclassify
n = 1capabilities to their remaining application; - build the product;
- run it.
The operator should not need to understand the implementation of the code being deleted.
If they have to ask what a function is and whether something else uses it, the architecture failed.
If they have to grep the repository looking for mystery consumers, it failed.
If they delete something declared specific to one application and a different application breaks, it failed.
If the application disappears from the product but leaves executable routes, helpers or production operations nobody knew belonged to it, it failed.
If the original developer has to explain how to extract the application, it failed.
The remaining product must work, and the removed application must be gone.
That's the empirical standard:
An operator who has never read the code can go in, hack the application out, and leave everything else working.
That is what it means to delete with extreme prejudice.
Yes, my rules are few and oversimplified, because every real case wants another rule — and by the time we reach 80 percent enumeration we have a decision tree nobody can decipher.
Discovered while trying to delete some DNS code.