Why OOP failed?

mmontoya
14 min readApr 27, 2020
This is the GEA 5600 dough feeding machine, it mixes, conveys, shapes and even decorates different kind of cookies.

tl:dr; After twenty six years as software developer, I’m convinced that OOP paradigm is an anti-pattern, so try another approach if you feel OOP is not for you. Many great developers actively avoid it these days. Try to be coherent-by-process instead of coherent-by-entity.

There is no doubt that OOP is a disappointing present reality. In some way that is remarkable, but not less wondrous is the fact that very few people are trying to improve the situation, that force que inquiry “Why?” Well, some economists would say that we are trapped in an “Qwerty” situation.

As you probable know, when typewriting machines were invented around 1860 they had a tendency to get jam if the user typed too fast, the manufacturers found an ingenious solution, they mixed the keys in a chaotic way to force the users to slow down the speed. When the jam problem was solved they couldn’t go back to the original alphabet order because at that point the users had already learned to type in what now we call a “qwerty keyboard”. A similar qwerty effect happened with cars, using a ton of metal that emits carcinogenic fumes to move a young and non-so-thin person for five blocks is a deranged idea, but we are unable to escape: we developed our cities thinking in cars and we are forced to use cars because the cities. We are used to thinking about people who lived in ancient times like doing crazy things, all without thinking that the future generations will hold the same fair opinion about us, because indeed we do crazy things. Then, OOP is also a qwerty, a whole generation learned to develop software under a wrong paradigm and now they don’t know how to do things under a different light. As the medieval peasants that accepted their faith with powerless resignation, the modern developer accepts his astray in silence, of course the main difference is the big check and Netflix.

But before continuing with the subject I should warm the reader that this is an entry with some personal traits. Since I was in high-school I’ve owned — as, I guess, many other people on this planet — a tendency to dig into the minimal elements until found, as Descartes would say, a perception “clear and adequate” about the problem. Everybody knows the Einstein’s quote that says that if you can’t explain an idea to a little kid you really don’t understand such idea. And that is true because when we are learning something new and non-trivial we present several examples to ourselves through the grasp process and at the end we finish with a set of ideal arguments. During the 2008 economic crisis a thousand of articles, several documentaries and four movies appeared but I couldn’t understand the causes and the development of the crisis. Tired of don’t understand I walked to the book store and bought a set books of macroeconomics and finances, after two years reading after the noon I finally could understand what Labour Productivity means (which is not what the regular public believes it is), what “short selling” means when someone borrows actions in the stock market and why the “Shadow Banks” were so toxic for the economy. By the way at the end of the process I lost a lot of respect for economist: most of them tend to ignore that the Economic Science is a branch of the Social Sciences and speak of “economic-models” as Astronomers talk about static and distant galaxies.

The same process, from a blurry cloud of words to a structured and integrated set of concepts happened when I studied Spinoza’s philosophy in my college years. Dominate something hard after a long, heavy and often frustrating process is one of the best and most rewarding things that we as humans can enjoy in our short life… and nevertheless that never happened when I developed software under the OOP paradigm.

I started developing software professionally using Perl in the 90s when almost all the Internet was made through CGI pages. I enjoyed it, I made some new friends and earn good money with that language but around 1997 it was clear that Java would be the dominant language in the market and to use it you need to learn how to develop under OOP. “Advanced Java 1.1 Programming” was the first of a dozen books about OOP related stuff that I bought through those now far-flung years. I understood the objects, the inheritance, the encapsulation, the polymorphism, none of those concepts is particularly difficult, but I never could grasp the general principles that should underlay the concrete application design. Besides, quickly I discovered that the only feel that Java waked up in my was a gluey tedious and an incipient hate. Heavy, hyper-verbose and stiffly, I was really happy when a friend asked me to help him with a very large and green-field CMS for a Mexican airline. I had to learn PHP over the progress, at the time PHP had a very brittle and basic OOP implementation, but that in some way helped me because I could explore and discuss OOP while it was introduced in a more robust way into the language. But even so I never could find the bridge that joins the academic-rooted ideas and the real life activities.

Anyway, after living some years at the 24/7 software world something was clear to me: coding has three levels: the function level, where you must flat a set of arrays or code a regex or reduce a hash or things like that, many times doing such tasks is fun and all developer must acquire some wisdom to find the balance between to be elegant but too cryptic or clear but too verbose. The second level involves to think the software as a group of defined and interacting components, as a set of entities that must communicate and work together. The third level is the “Application in the World”, this level establish the ways the product will communicate with the “external” world: the database, the OS, the Internet, other applications, other APIs. All three levels are important and require big attention and care, but for my — and by far — the most important level is the second, I’m convinced that the fail or the success for any product lies in that section, the part where the “Integration Design” establish in a robust way how the Domain will be understood in terms of abstractions. Technical Debt owns a toxic nature, like those bugs that set their eggs in a living creature it really kills products and companies in a silent, slow and painful way; because of that, a product can only takes some certain amount of it. And the real TD lies in the second level. Refactoring in the first level is relatively easy because the scope is limited, but to try to refactor a component or a service usually includes a Domino Effect in other parts so it requires a whole discussion with the team. As PHP, JavaScript or Microsoft Windows can attest, get rid of bad initial designs can be a real hard — sometimes impossible — task.

In 2006 everybody was talking about a great new thing called Ruby On Rails, with an MVC design pattern and a language where everything was an object it looked like a really good deal. As many others I switched to RoR and so Ruby took the place as my main language. Since this language was a really an OOP tool I was hoping that in a near future I would have this long yearned and orgasmicosmic “Aha! moment” when I could see the Matrix and finally I could be spellbound into the final and definitive OOP epiphany… but ten years passed and nothing of that occurred. Yes, Ruby has a kind of smart syntax, but all that is located into the Function Level, in the other hand MVC pattern was a clear advance because it helps to shape the initial state, it put things in the right track, in the same way that collecting bricks and cement is a good idea if you want to build a house… but the house and the blue print of the house are still not there.

Several books about “Design Patterns” appeared in the shell of my bedroom, “Decorator”, “Observer”, “Factory”, “Singleton”, I understood what they are and how to use them but I also realised that many of them are “Post-Mortem” solutions, I mean, in few occasions they appear in the Design Stage but the truth is that most of them appear after the first version of the system is finished and deployed, only after the programmers know the code and the Domain in a very deep and robust way developers start thinking in a specific Design Pattern to refactor some particular section of the application, that deep level of understanding and confidence rarely is seeing in the first versions of any product. Instead, what I saw over and over through the years was the use of Classes as a chaotic and semi-intuitive “semantic bags” where a lot of things that sound kind of related where put together. Reading about and creating a lot of UML diagrams didn’t help either. Around that time I read “Domain-driven design” by Eric Evans, it’s a great book — now an obligated reference for any software developer — , reading it I felt many times that the author shed light on many issues that remains “untalked” and ignored under the design time, overall the structural importance of the Domain and its “reflex” in the concrete implementation was finally settled. But even after reading it I couldn’t found the definitive guide to abstract an specific Domain. At this point I was beyond frustration, instead, a certain bitter resignation settled in my soul.

In 2015 blogs entries about Functional Programming started to appear across the web, a lot of them. I’m used to hypes, I remember the huge hype about Microsoft .Net in 1999 when nobody knew exactly what .Net was, that Seattle marketing office certainly did a good job. At the end it turned out that MS had cloned a Java that only runs over Windows, they called it C#, the hype disappeared in weeks. I remember the 2004 hype for SOAP, “If you don’t know SOAP your job is at risk” you could read in some tech magazines. Also I remember the 2008 buzz for MongoDB, and I remember thinking after using Mongo for some weeks: “No way I’m throwing away half-century of SQL code, features, robustness and research for this thing”, and I wasn’t wrong. So with some resignation I started reading about FP, which of course addressed me to a great and pleasant afternoon drinking tea and reading about Alonzo Church and his Lambda Calculus. The theoretical links between Functional Reduction and DNA replication added a stimulating element to those readings. Since many examples were made using Lisp, I installed the SBCL implementation of Common Lisp, but I found the tooling and the ecosystem kind of rigid and outdated. Anyway my area of expertise has being always the web, so I installed Clojure to develop the classic and initial “My blog” exercise. In just one weekend I was totally engaged. All that was so new and weird but at the same time so simple. After some weeks I developed a new pairs of eyes, all my works as programmer rejuvenated, it was like I just left the college again. And.. wait, wait a minute! Where the classes are?… what the heck do you mean with there are no objects? Since 1996 everybody has told me that the only good way to create software was using objects, the greatest and smartest developers use OOP, Objects are the best thing ever, they are easy, they guide you to good designs and good abstractions. Well, now I know that’s a lie, instead I can say that OOP *can* lead, under the correct circumstances, to a good product, but that can be achieved also through FP paradigm and even under structured code like it that was made in the 80s. For twenty years I was wrong, but now I could see the truth in a clear and solid way: I never had the OOP epiphany simply because there is not any OOP epiphany at all. An epiphany includes the sudden grasp of an axiomatic certainty, and there is no such thing under the paradigm. At the best, OO Modeling will remain a perpetual approximation. Even worst: OOP tends to concreteness and obstacles good abstractions.

OOP was an important mistake for the software industry, the initial and more important promise of OOP was that using it we can think about software in a natural and intuitive mode. Clearly that promise was never delivered (and never will): the spaghetti code and messy designs show a vigorous life under OOP as it did under the Structural paradigm. Several serious researches have being done trying to measure the software quality under OOP, the results are clear: software developed under OOP is not better than others paradigms as the structural or FP. Even more: in a consistent manner, software developed using two non-OOP languages, Clojure and Haskell, delivers better quality and fewer bugs, but that is probably because many old farts and experienced programmers (like uncle Bob) end using those languages instead of Java or Python.

There are several reasons why OOP failed, among the most important was the vagueness of the “Object” concept, because the astonishing truth is that until today nobody knows exactly what an “Object” is. In some books they are defined like a Data Structure, but others talk about them like containers of data and behaviour, others prefer to use them as “places of state”, for others they are “conceptual entities” in a linguistic-like way. It’s worth to mention that the father of the OOP paradigm doesn’t consider Java, C++ or C# as a real OOP languages. In the other hand, one of the causes of why many young students struggle with OOP is because many books start with the classic: “Car is a class, Ferrari is an instance of that class or Object, it could have attributes (it’s red) and behaviour (it accelerates)”, then real life appears, there are no cars or Ferraris anymore and mess is the norm.

OOP was over-optimistic when it assumed that we can use Classes in the same way that we use our daily words. Around 1917 Ludwig Wittgenstein declared the end of the Philosophy, any Philosophy; he did that because he found — while fighting against the Italians in the Alps — that our words were so complex and vague that to try to establish a logical meaning with them was a worthless and vane effort. Any attempt to define them is doomed to fail. I’m sure that the initial proposers of OOP would have second thoughts if they’d have read the Tractatus. Many software projects have a “User” between its main classes, sometimes the User instance can get the Customer role; sometimes we want to see the Customer as a fiscal entity in order to charge his taxes, but sometimes we want to think the Customer as a person (from the marketing point of view) to send him an offer in his birthday. Words are “polysemic”, id est, they have a number of meanings, interpretations and understandings, but Words are aso a live and endless process, knowing a Domain changes the Words and so changes the Classes that try to catch the concepts behind the Words. Then OOP Classes are also polysemic, but in their case the context that helps to give their sense are harder to infer, for the new arrived developer the methods “send_taxes” and “send_birthday_offer” are just there, in the Customer.java file.

In this regard Domain-driven Design (DDD) is a great improvement because it offers a concret method to follow and makes many things more explicit, but you don’t need OOP for using DDD. In fact the structural and final design can be easier to express in FP than in OOP because in FP the whole second level of coding, the component integration, is easier to change and has less “Domino effect” that in OOP, this is because in FP the basic unit of coding is the Function, and functions for their own nature are isolated, they are small and stateless black boxes, Objects, by the contrary, have a complex and full of state inner life.

The other reason of the OOP fail was the over-optimistic and naïve assumption that semantically complex concepts in the Domain can be translated in tidy and harmonic Objects into the model. Usually that ends in a huge conceptual (and worst, implicit) layer between the Domain and the implementation. Astonishingly, OOP fell off in the same ditch that one thousand years before Meddieval philosophy did aswell: OOP coders are totally convinced that by determining the secret meaning behind words, they could become acquainted with the facts.

The truth is that OOP, for the way it conceptualises components, is highly prone to conduce to a big, multi-directional and labyrinthine set of entities, all of them with their own changing inner life. In this scenario, expensive technical debt is almost inevitable and besides, it’s very hard coded from the beginning. The original OOP conception had in mind a set of small objects, no more than twenty lines of code shaping a couple of methods, those tiny and tidy objects should not hold any state and they must send messages one to another, how the heck that is related with those Java’s monstrous four hundreds lines classes with twenty-fives methods and fifty imports? The results of than design is a heavy cognitive load that lies in the shoulders of the poor developers, day after day they most keep in their minds a group of huge entities and several places inside of them before be able to change a single line. Their life would be a lot easier and much more fun if they could think in terms of some smalls, immutable and isolated creatures, you know, things like functions.

There are some advantages of OOP over FP though, for instance there are more semantic meaning in Objects than in functions. But at the end of the day is just much easier to think software like a unidirectional thread of functions that changes a well known Data Structure than a multi-directional set of Objects that change their multiple states and send messages between them back and forth. The importance of this point cannot be overstated: FP is a unidirectional and isolated thread of modifications, OOP is a multi-directional set of sprawled points of potential changes. The first is easy to understand and so to change, the second remains anchored in its initial shape through the time because understand and change it is expensive and hard. So at the end the semantic advantage of OOP is diluted.

We like to think that in our life as developers we must deal with complex, subtle and convoluted ideas to achieve solid systems, after all an aura of smartness surrounds our software jobs. But we can think our daily duties in a more effective and prosaic way, which leads us to the beautiful GEA 560 cookie machine. As the machine, we carefully design our raw materials (data structures) in the top, convey it through different transformations and at the end we deliver a final product (another data structure) in the opposite extreme. Modeling a Domain is about processes, not about nebulous “classes”. We want to capture what a Domain does, we don’t care what a Domain is. A function transform the data structure in an Only-one-way and then pass it to the next function. That’s all. OOP is about defining scholastic and big entities, FP is about Data Structures and processes. Not a surpirse why FP delivers better software. For two decades I couldn’t answer the question “What, in an essential way, developers do?”. Now I can answer that question: We build structures to feed transformations, we understand a Domain and then we express the Business Logic as threads of functions that convey and transform the initial data structures, it’s simple, it’s more efficient, but overall, it’s more fun.

--

--