I can't help but want OOP for situations where encapsulation is helpful. Creating boundaries around code with similar level of abstraction is useful. The opposite is what we have in Typescript - every file is a bag of functions and data with no clear distinction between parts that may do file IO and business logic.
After re-reading my whining, maybe this is more of a "Tell HN". lol.
I've found that OOP has too many footguns and becomes a nightmare to debug. in contrast, I find that e.g traits in Rust (insert equivalent for your language of choice here, I'm aware Rust isn't the only one with it) are a much cleaner fit. I'm not really sure what the official term is, but I refer to it as Composition-based programming. It sits in a perfect middle ground of being object-oriented-ish without letting people go hog wild.
This all said, I find codebases that use only "functions & data" to be very cumbersome as well. We have patterns for encapsulation that have kind of gotten thrown out the window over the years and it's frustrating. Too many of these codebases feel like they have an inadvertent bus factor of one due to the code being so un-encapsulated, and it's one or two people who really know how it all works.
(As a side note, I never got the hate for JS classes - I know they don't behave like true classes in other languages, but I considered that a plus. React was 100x more approachable with them.)
Personally, I have often found that once you try to scale complexity much past the canonical "Student is-a Person" example, it's extremely difficult to avoid violating SOLID principles, and things turn to a big ball of mud pretty quickly.
This is mostly due to inheritance being way overused. Even the initial creators of OOP stated "prefer composition over inheritance" [1], but this seems to get forgotten more often than not. Couple this with Javascript's well known issues with the "this" keyword, and you've got a recipe for disaster.
I avoid the OOP "pillar" of Javascript [2] for these reasons, plus the fact it just isn't required - I never wonder whether the caller has messed with "this", never need to use "bind", etc. etc. "Scoping by function" is a concept I'll blog about sometime soon.
[1] - https://en.wikipedia.org/wiki/Composition_over_inheritance [2] - https://medium.com/javascript-scene/the-two-pillars-of-javas...
We have some React class components in our system. People who are < 3 years into their career don't like them as they've been told they are complicated and generally considered harmful. Over time, these people tend to come around and realize that class components can be very useful for more complex components.
On the other hand, functional programming had the right mathematical idea. No one needed object oriented mathematics, and there's no object oriented physics. (At least, not in any way that's dialectically useful to me in this moment.)
I think the proper place is a healthy middle ground, with capability for both to be mixed and matched as needed.
you get a clear boundary around useful modules when you have good design. you get clear distinctions with good naming and organization practices.