While I totally agree on the point of there not being a silver bullet and a hybrid approach is a solid choice in most cases, I can’t help to notice a lot of inaccuracies in this article. Some random points I noted while reading:
“Think of it as the discount version of thread safety.” No, immutability is just one of the ways to achieve thread safety. Thread safety is just the property of ‘a thing’ to be safely used from multiple threads (commonly data structures). This property can be achieved in many ways and it just depends on your exact situation what the best one would be. The connection between immutability and thread safety is that every immutable data structure is by definition thread safe (ignoring broken sharding algorithms). So it isn’t a ‘version of’, but rather a ‘way to’. It is also a bit strange to talk about threads in the context of FP. In FP concurrency is usually done in more higher-level abstractions, for example with continuations or coroutines. And it is those higher abstractions that provide a lot of ease in which you can do concurrency in FP. It is also not so much parallel programming that is easier in FP, but concurrent programming.
“The OOP style is more like a recipe and can be seen as a series of steps rather than as a series of calculations to be carried out.” No, your OOP code shouldn’t be like a step program. If that is the case you are doing it pretty wrong (which, tbh isn’t that rare in ‘enterprise’ OOP applications). What you describe is imperative programming. In OOP you have objects containing responsibilities that send each other messages. That is what you should see: the definitions of objects, in most modern OOP languages with classes. If it is just routines or procedures it isn’t OOP. Even if it is wrapped in classes, syntax doesn’t make paradigms.
“Major programming languages like C++, Java, PHP, C#, and Python is based on the object-oriented approach.” Not really, both PHP and Python were imperative languages which got OOP concepts added to them over time. Also note that it isn’t just OOP that uses objects, FP and basically every paradigm has objects. It is just how computers work these days with the stack and the heap.
“You use OOP to break complex problems into less complex problems. Solving them separately in units of code.” This is not a difference between OOP and FP. Every programming paradigm I can think of has some way of splitting things up and then merging them back together again. Defining abstractions and the combining of abstractions are the fundament of all off software architecture. Those principles run way deeper than paradigms do. The difference is in how you create those abstractions. In OOP you create objects that have responsibilities and communicate with each other. Usually with a form of object blueprints so there isn’t a direct link between the existence of an object and its responsibility. In FP you create functions that you compose together via mathematical operations. A paradigm is just the practical approach to the ‘how’ of coding.
“At this point, polymorphism seems to be crucial in software development.” It indeed totally is, but it isn’t limited to OOP. Actually, the class-based inheritance model is way more limited when it comes to polymorphism than the structural typing of FP languages. Overriding methods isn’t polymorphism, but dynamic binding. Polymorphism means that a value can be seen as a member of different types. For example, a Cat is also an Animal and an Animal is also an Object. This is not related to overriding methods, even without them it is still polymorphism. While in class-based OOP languages you can only have polymorphism down the explicitly defined inheritance tree, with structural typing this is achieved implicitly without the need for the programmer to define all the possible types of an object. This makes polymorphism in FP a lot more flexible and powerful.
“Object-oriented programming is usually the most natural and sensible approach. OOP languages allow you to divide your software into medium-sized problems that can be solved independently.” Outside of what I already said 2 paragraphs up, the size of abstractions doesn’t depend on the paradigm. With FP you type abstractions, create modules and compose them together. In this regard it is no different from the interfaces and namespaces from OOP. The size of abstractions just depends on the chosen technical design, not so much in how you actually go around implementing them.
“It is possible to achieve 100% abstraction by using interfaces.” Of course you can. An interface is just a type declaration. Again, this is not a difference between OOP and FP. In FP you also abstract over type definitions. Just like in imperative programming, procedural programming or logical programming. It is almost like the concept of abstracting over a type isn’t limited to a single programming paradigm.