Definitions abound. One, given by software éminence grise Martin Fowler in a 2015 talk, holds that architecture is “the important stuff — whatever that is.” Or, as a gloss on this same talk has it, “anything that’s important and hard to change.” Much to the chagrin of a certain stereotype of a software engineer, this is a wholly social judgment. There’s no hard-and-fast rubric for what makes something important; the important stuff is whatever those who know the code best agree it is.
The cheekiness here is useful for bringing architecture out of the highfalutin rarefied stratosphere, but also a little frustrating: it’s hard not to feel as though Martin knows something he isn’t telling us.
In software as in life, one thing that winds up being true about architecture is that it’s what you live in. Think about the code you interact with on a daily basis. Does it feel good? Is it easy to take pieces out and put pieces in? Does reading it yield a pleasant sense of order and clarity, or a discomfiting sense of chaos and muddiness? This is a little loose, as criteria go, but it works. It’s also easy to get a grip on: when talking to people who have some experience with the warp and weft of a codebase, they’ll know its tactile qualities more readily than they’ll know whether it’s well-architected in the abstract.
For my own part, when I’m kicking the tires of a new codebase and trying to evaluate its design, I look for boundaries and contracts. Boundaries are the liminal spaces where one part of the code touches another; practically, they’re where you’ll see patterns like exports and imports (in some languages), module declarations (in others), and filesystem demarcations (folder structure may not be the whole thing but it’s still a thing). Contracts are crossover spaces where one part of the code expects something from another; practically, they inhere in patterns like inputs and outputs, request and response shape, and ORM object structure.
More formal definitions often reach for a notion of a system’s “fundamental” or “highest-level” components. But this tends to draw focus to features like stack and language choice that bear on the architecture but couldn’t be called architectural per se. (A JavaScript project will tend to separate its modules into filesystem directories where an Elixir project may not, but both can and probably should be architected in a modular way.) At the same time, definitions like these pull focus from micro-level design decisions that have an outward-rippling effect on how the system is built.