|
> The point of abstraction is to limit blast radius of requirement changes. No, the point of abstraction is to make things easier to handle. At least that is the original meaning of the term, before the OOP ideology got its hands on it. A biology textbook talks about organs before it talks about tissues before it talks about cells before it talks about enzymes. That is the meaning of abstraction: Simple interface to a complex implementation. In OOP-World however, "abstraction", for some reason, denotes something MORE COMPLEX than the things that are abstracted. It's a kind of logic-flow-routing-layer between the actually useful components that implement the actual business logic. And such middleware is perfectly fine ... as long as it is required. Usually it isn't, which is where YAGNI comes from. Now, pointless abstractions are bad enough. But things get REALLY bad, when we drag things that should sit together in the same component, kicking and screaming, into yet another abstraction, so we can maybe, someday, but really never going to happen, do something like rename or add a field to a component. Because now we don't even have useful components any more, we have abstractions, which make up components, and seeing where a component starts and ends, becomes a non-trivial task. In theory this all seems amazing, sure. It's flexible, it's OOP, it is correct according to all kinds of books written by very smart people. In reality however, these abstractions introduce a cost, and I am not even talking about performance here, I am talkig about readability and maintainability. And as it turns out in the majority of usecases, these costs far outweigh any gains from applying this methodology. Again: There is a reason YAGNI became a thing. As someone who had the dubious pleasure to bring several legacy Java services into the 21st century, usually what following these principles dogmatically results in, is a huge, bloated, unreadable codebase, where business functionality is nearly impossible to locate, and so are types that actually represent business objects. Because things that could be handled in 2 functions and a struct that are tightly coupled (which is okay, because they represent one unit of business logic anyway), are instead spread out between 24 different types in as many files. And not only does this make the code slow and needlessly hard to maintain, it also makes it brittle. Because when I change the wrong Base-Type, the whole oh-so-very-elegant pile of abstractions suddenly comes crashing down like a house of cards. When "where does X happen" stops being answerable with a simple `grep` over the codebase, things have taken a wrong turn. |