Architecture and ontology
Why dividing a system into modules is always a choice about how the world is built, and why an engineer had better admit it.
The architecture of a software system looks like a purely technical matter. Where to draw the module boundaries, what to separate from what, which entities to put in the database, which service owns which data. After seven years at a bank I've gotten used to treating these as engineering questions, the kind with better and worse answers you can measure in latency and maintenance cost. The longer I do it, though, the more clearly I see the other side. Behind every such decision hides a claim about the world: which things exist on their own and which are connected. Architecture is applied ontology, even if nobody says that word at standup.
A module boundary is a claim
When you decide that "user" and "account" are two different things or one, you're structuring more than code. You're asserting something about the reality you model. Can a person have several accounts? Does a user stop existing when the last of them is deleted? These questions look technical, but at bottom they're ontological: what counts as a separate thing, and what counts as a property of one.
At a bank the abstractness of all this disappears on the first day. We had a users table where a row was a client, and a contracts table where a row was a product. For a long time it seemed the person just was a row in users. Then came an integration with another bank's system after we bought a portfolio, and it turned out that one and the same living person existed with us in three copies: three client_ids, three slightly different full names, one passport. Compliance required us to treat this as a single individual. The schema counted three. The argument over whether to merge looked like a deduplication task, but what we were actually settling was where, in our universe, the line runs between a person and their traces in the system.
The model becomes reality
The most important thing happens later. Once fixed in code, a model stops being one of several possible descriptions and becomes the reality people live in. If the system never allowed for a person to have two citizenships, two names in different alphabets, a biography that doesn't fold into a single straight line, those possibilities simply won't be there. Not because they're impossible in life, but because they're absent from the schema. (That's the one "not because, but because" I'll allow myself here, and I can't get by without it.)
I've seen this literally. In the support back office the "citizenship" field was a dropdown with a single value. An operator taking on a person who held two passports physically could not enter the second one: the interface wouldn't allow it, and behind the interface sat a citizenship column of type varchar, not an array. The client was politely invited to "choose a primary one." In effect we were asking a person to shrink himself down to the size of our cell. The categories of the database became the categories of his experience of dealing with the bank. Fixing it took a migration, a separate release, sign-off from legal, and three weeks, all so the world would once again permit the existence of something that existed anyway.
There is no neutral decomposition
Engineers are taught that good decomposition is "natural": the right boundaries are supposedly built into the domain itself, and you only have to make them out. It's a useful illusion. The same domain can be sliced a dozen ways, and every slice lights something up while turning something else invisible. Choosing the boundaries is choosing what matters, which makes it a value decision dressed as a technical one.
Here people usually push back, and the objection is a serious one. We do have Domain-Driven Design, bounded contexts, ubiquitous language: the language of the business dictates where to cut, and the engineer is a translator rather than an author. I worked exactly this way for years, and I think the approach is more honest than most of the alternatives. But it doesn't dissolve the problem, it hides it one floor up. The domain language isn't a fact of nature; somebody said it first too, a product manager, a regulator, an analyst who had his own picture of the client. When we carefully encode "the language of the business," we also encode whose business it was and who went unheard in that language from the start. DDD disciplines the translation. It doesn't undo the fact that you're translating out of somebody's dialect.
The flag that lived a year
For years I built out our Unleash, a feature-flag platform. A flag is a fork in the modeled world: switched on, one reality; switched off, another. The fork is supposed to be temporary. In review we even agreed to tag flags with a death date.
One flag lived with us for a little over a year. It turned on new limit-calculation logic for a narrow group of clients and was meant to vanish a sprint after the experiment. The experiment ended, the author moved to another team, the removal ticket drifted into the backlog and dissolved there. Over the year the code under the flag grew dependencies, two other services came to reference it, and at some point it turned out you could no longer switch the flag off: half of production silently treated its behavior as the norm. A temporary fork had quietly become the only road. Nobody designed it as a permanent structure, yet what ends up being structure is whatever stays switched on longer than anyone remembers why. Working through it on call at three in the morning, I couldn't shake an unpleasant feeling: our ontology at that spot had come together out of forgetting, with no decision behind it.
Why an engineer needs this lens
So as to design more honestly. An engineer who sees only technique in architecture treats his decisions as neutral and so feels no responsibility for them. An engineer who understands architecture as ontology asks it differently: whose reality am I encoding right now, which cases am I making impossible, who doesn't fit my schema and what becomes of them?
Though honestly, I'm not sure this lens is one to wear without ever taking it off. Sometimes it gets in the way. If at every column review you keep in mind that you're drawing the borders of somebody's world, you can freeze up, and the business wants a release on Friday, not a seminar on the philosophy of the subject. There are days when I think a varchar is just a varchar, and I'm right to think so. So I'm not calling for permanent anxiety. I'm calling for something else: at least sometimes, in the decisions that are hard to roll back later, stop and ask what exactly we're making exist right now, and what we're leaving out. This is where my engineering and my research meet most closely. When you split a system into modules you're choosing how its patch of the world is built, and it's worth making that choice with open eyes once in a while, because otherwise a flag someone forgot to switch off will make it for you.