Why The Phrase "Over Engineered" is a Bad Wrong

Never tell me something is “over engineered”. Complex problems often require complex solutions and attempts to simplify them often lead to opaque and rigid systems where the complexity is hidden, not only from end users, but also peer developers and our future selves, rather than properly declared and then reduced for the 80% with sensible defaults and templates. This means that every time the code is introduced to a new developer or returned to after some time, the solution effectively needs to be reverse engineered to be modified. You have to look at the imperative implementation code to understand the model , inputs or outputs. Because “Declaring” your contract with schema was “ too complex”. Because drafting your design first in PUML to make sure you understood how everything worked together was “over engineering”.


NOW, that said, often when people describe a system as over engineered, what they actually mean is overly redundant. Meaning hosting infrastructure, containers, shared resources ( db or event pipelines..etc ) are copy and pasted between every distinct concern.

But to be clear this is not “over engineering” this is a lack of engineering. It’s a lack of systems resource optimization. There’s not necessarily anything wrong with putting too closely related services in the same container, or sharing the same kafka instance, as long as they have similar compute needs and importantly, as long as you declare the contracts between them clearly even if they are in the same container, so that they CAN be separated if they need to be.


THERE ARE TIMES though when you DO want to separate teh services, even if they are very small. Generally because they have different scaling needs, release cadences, availability demands..etc For example, it’s not a good idea to mix long running task processors with state transfer services. One needs to be highly responsive and will see highly variable demand. The other is generally fine to take a controlled number of tasks at a time and not respond until it’s ready to take on more work. So if you have only 2 small services, but one is responding to user events in a blocking, “they need a response right away” manner, and the other is taking long running work off a queue, then they can and SHOULD be in separate systems and this is NOT overengineering.
The key is to understand WHY you are separating systems, or why you are keeping them together.

I once read something to the effect of “Microservices should be as small as is prudent but no smaller”. That’s not the exact quote, if I can find it I’ll correct this. I believe it was in “Building Microservices” by Sam Newman. Though it may have been something out of Effective Java. But either way, it’s super arbitrary and utter nonsense. Microservices should be as big as they can be before they overwhelm the shared resources they rely on ( such as DB, Kafka..etc ) or they require more engineers to maintain them, than can effectively communicate with one another without slowing each other down. That depends a bit on how well architected the service is but it’s not arbitrary . A well designed system that is highly polymorphic, highly declarative, with lots of code generation and meta programming, may offer a very large domain model worth of features with very few engineering resource demands , and so may be deployable entirely within a single bounded context, whereas a system of similar feature complexity, but poorly architected, may require far more engineers to maintain, each of which have to be somewhat aware of what all the others are doing and so should be broken up with firm contracts between all services. Of course the nature of such systems is that they typically don’t get broken up… they grow… And if they had good contracts, they likely wouldn’t be considered “poorly architected”. So the systems that most need to be broken up to avoid communication bottlenecks and cognitive overload, are also the ones that aren’t going to be.

But that’s really the root of it I think. The reason we have so much trouble locking this down is because it is a dynamic, multi-dimensional problem. The answer is “ it depends”. It depends on the quality of your architecture. If your architecture is good you can build bigger services. If your architecture is bad then you should limit services to what a small team of engineers can maintain. When I say small, I mean small enough that they can keep track of one another. This isn’t a hard number but it’s in the realm of the amount of people you can have a meaningful conversation with in the same room.

Comments

Popular posts from this blog

Define "Scalar Value"

AI Self Awareness in Video Games

The World of Conceptual Data Modeling