Object Oriented Programming

... Has little to no value. Keep on reading and I'll explain how you can save precious development time avoiding objects and classes and just using functions and structures.

I used to drink this kool aid of OOP, encapsulation, polymorphism, interfaces and so on. And I still probably drink some other kool aid which I am not aware of yet, but the ones I do get awaken from I want to tell others so that they may be awake too.

If you're a Java developer (and there's a huge percentage of them), well, chances are you're ensnared with this as well. And Java makes it even hard to write stuff without objects so it's even harder to get out. Nevertheless, if you have to work on Java, knowing this and suffering (ignorance is a bliss) is better than drinking kool aid and not knowing why your software development time is so slow for even the smallest matters.

This may sound controversial, but I'm certainly not the only one who thinks of this, consider quotes of these legendary hackers (from http://harmful.cat-v.org/software/OO_programming/):

“Object-oriented programming is an exceptionally bad idea which could only have originated in California.” – Edsger Dijkstra

“object-oriented design is the roman numerals of computing.” – Rob Pike

“The phrase "object-oriented” means a lot of things. Half are obvious, and the other half are mistakes.“ – Paul Graham

“Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function.” – John Carmack

“I used to be enamored of object-oriented programming. I’m now finding myself leaning toward believing that it is a plot designed to destroy joy.” – Eric Allman

"The object-oriented model makes it easy to build up programs by accretion. What this often means, in practice, is that it provides a structured way to write spaghetti code." - Paul Graham

You see, majority is usually wrong. Majority opinion doesn't mean much. Ask an average person on the street about how column store works and they will not know. Neither they should. You might say, "oh well, they are not software developers though", and you're right, but same analogy does apply within software developer community regarding OOP. Most functional programmers got there for a reason and cannot even look back at object orientation. Yet if you asked a vote of most software developers today on whether OOP is good or bad most would say it's good, without even knowing the world of functional programming. Just like democracy cannot work, same way majority opinion cannot be trusted when it comes to issues which require years of experience, pain, and understanding (which majority doesn't have).

First, I want to explain two generic principles, that apply pretty much everywhere in life, and then it will be much clearer why OOP is such a bad idea.

Lack of information is unprofitable

If you knew that a certain cryptocurrency will go up in price 10 times tomorrow then you would quickly buy it today and reap all the benefits. If you knew that a plane will not take off you would not go to the airport that day. If you knew that you're about to get fired from your job you'd start looking for another job right now. If you knew that someone is planning on stealing your car you would park it somewhere else. If you knew that your computer has a keylogger in it you would not log into your bank account. If you knew that your bad health is caused by a lack of a certain nutrient you would fill up on it. If you knew where good pizza is made in a new town you wouldn't try a random poor place that you don't know. If you knew that your ad campaign's ads are most effective in certain hours of the day you'd run ads on those hours. If you knew that a certain road had a car accident and is now jammed you would not take that route. If you knew what your wife likes at a restaurant you would order that thing and not what she doesn't. If you knew that your car has a defect that will make it to stop soon you would fix it right away.

What is the generic principle here? The more you know, the better and the more specific decisions you will make. If you know about low level computer architecture that will make you a better software developer. Same with knowing networks, expected traffic, what kind of database do you use, what kind of library runs underneath. And, VERY IMPORTANT, any piece of information that was unknown, which becomes known has a chance to turn around your decision 180 degrees (consider knowing that a plane won't take off example).

Consider a fast lamborghini. Entire car was tuned for a single purpose, drive fast and look good. That means entire body was shaped with low resistance aerodynamic capabilities. That means many parts are made from a lightweight carbon. That means engine is very powerful. That means there are not many places to put stuff in as this is not a family car. That means car has a tight suspension. That means car has a low bumper. Would any sane person consider driving lamborghini in forests and off-roads? Probably not. The car was not built for this, it was built with an INFORMATION in mind that it will be a sporty roads car. Every part was compliant with this purpose.

Now, consider an offroad SUV, like Mercedes G class. Completely different profile, tires are large and soft, suspension is much softer, bumper is much higher than the ground, and frame is solid and heavy. It was built with an INFORMATION in mind that it should go offroad and handle all the challenges. It has a lot of storage in the back. Every part was compliant with this purpose.

I did not check explicitly, but I'm pretty sure that there is no part that could be put from Lamborghini to Mercedes G class and from Mercedes G class to lamborghini.

This is the car that today's Java software developers would make:

  • We would have generic wheels reusable everywhere with same interface and attachment
  • We would have generic body reusable everywhere with same interface and attachment
  • We would have generic engine reusable everywhere with same interface and attachment
  • We would have generic seats reusable everywhere with same interface and attachment
  • We would have generic transmission reusable everywhere with same interface and attachment

If you need an off-roader and you need much bigger wheels, tough, you can't have them because they don't fit generic car's body. If you want to put a very large engine in, also tough, because the engine does not fit in generic car's body. If you want to transport many things with a car and you need a larger trunk, tough, because generic body does not support that.

In short, car built for such principles, with generic parts that try to fit along same interface would be good for no single purpose, and any car with specific parts tuned for that purpose would beat the generic car in every way.

Same with animals, most of mammals have eyes, hands, feet, fur. Yet all eyes are different, all feet are different and everything in an animal seems to be tuned for a general purpose of that animal.

Which brings us to another point:

Specific solutions are superior to generic solutions

Just about every thing, that was tuned for a specific purpose is better than a generic thing that tries to accommodate for everything. Probably most of us have met at least once the interface of something, that was supposed to be generic, yet, changing implementation doesn't match the interface and interface needs to be changed. Interface by definition ought to never be changed, but yet they need to be changed on a daily basis. What a waste, had you just wrote a function without an interface, it still would have needed to be changed yet you would have no interface.

For instance, postgres is quite generic, yet it is powerful enough to tune database exactly for your needs. It gives you power to implement fast queues with publish/notify, it gives you power to create indexes for tables, it gives power to have json columns in a database, it gives power to have arrays as a table field, gives power to do view/materialized views which can be refreshed at demand. Yet no database will perform at its peak without specific tuning based on knowledge on what fields will be accessed by what keys. Postgres is amazing, but if you'll create a generic users table, with unindexed first name field, and will search by first name, there will be no miracle - search will be slow. You have to tune your database schema specifically for your needs as generic way of declaring tables will not usually cut it. Postgres also gathers information, binds to statistics of the table to possibly improve queries.

Or, consider C compilers, they know which architecture they are compiling to, they know instructions specifically and perform optimizations specifically. There is no generic way to produce native code, compilers inspect context and try to produce as much optimization as they can. If they did not do that we would be very disappointed in native code performance.

Or, consider architecture of aerospike key value store, it holds all keys indexes in RAM for lightning speed lookups. Creators of aerospike intended for index data to be in RAM. Or, aerospike also can plug in directly to SSD bypassing operating system layers to talk to the drive directly. That optimization is very specific to this key value store and achieves great performance.

Or, a real life examples. Say you have a servant for quite some long time. Chances are, if you ask something to eat, and your servant knows you hate broccoli, he will never prepare you anything with that. Probably because you had conversation about this once when he did and that made you upset. Also, your servant should know by now when you come back from work, what events you might be interested in, how often to wash the car, what clothes do you like and so on and so on. If we ever made a robot that could fully replace a servant (like in Detroit: Become Human) it would be completely useless if it could not learn from your specific information. What generic task could a robot servant do well that is not based on some kind of information about his master? To vacuum the floor? How to ensure it will not disrupt and not disturb you during the day? It has to know when you're not in the home, the information has to be there. So, servant to be useful MUST bind to the information regarding his master and environment.

Or, a work example. When you join a new job there are usually onboardings so that you would know most about the company and could have a full experience. Same with adapting and learning the tools that you will be working with, specific to that company. You would be a useless worker if you could not adapt to use some specific tool in a company and your brain will not bind itself to that specific tool.

So, the question is...

How can anything really be generic? Or is it possible to predict an interface that will never change? The more you expose in an interface the more specific it becomes and the lower level features of implementation you need in an interface the more your interface will look like specific implementation.

Sure, you might get lucky on certain interfaces, for instance, hashing algorithm interface which accepts byte array and returns a string. But even then, there are many specific implementation details, like:

  • Seed of the algorithm
  • Maybe you'd prefer low level byte array returned instead of a hex string
  • What if hashing algorithm doesn't return hex string at all?
  • What if you're in a tight loop and you'd prefer hashing algorithm not allocating return byte array but write it in place in a specific point in memory?

Even for such simple on the surface interfaces we can think of specific cases where interface would not fulfill our full desire in a certain situation.

How this relates to OOP

There are four pillars of OOP (or, I'd rather call them, pitfalls):

Polymorphism

Having array of Animals when it can be a Cat, Dog or an Elephant. Problem with such an array is that we got forced into a lack of information, we know it's an animal but we do not know what kind of animal. And our interface may be very poor and have one function that tells animals to make a back flip.

What inevitably happens in object oriented systems sooner or later, some code will have an array of animals, and animal interface will not fulfill the desire of the user, so, they will do one of the two workarounds:

  • Add new method to the interface for one use case and break all the interfaces
  • Check instance with a comparison if it is actually a Dog and if it is do something specific in that place (and violate this "neat" encapsulation)

Both of these incur more code change than had this been an enum with a matcher in OCaml

Failures

Generic abstraction failed and still needed to be specific. This was caused by a lack of information from an interface designer of how instances of these interfaces will be used.

REMEMBER FROM EARLIER : lack of information is unprofitable and specific solutions are superior to generic solutions

Inheritance

Inheriting a class to write less code maybe? What inevitably happens with class hierarchies is that you inherit more functionality than you need. Or, maybe superclass you're inheriting inherits something you don't need and you get a large code bloats and class hierarchies. And all of a sudden you need much more RAM because many more fields need to get instantiated from the superclasses which might be extraneous anyway. More RAM means that less will fit in cache and code will run even slower.

Inheritance bloat is so common, that this problem was identified quite early, the first occurrence I know was from Herb Sutters Exceptional C++ where he advises to use composition, having objects you need as fields instead of inheriting so you would have less bloat. Rust threw away inheritance completely (and boy, my heart fills with joy when I remember that) and never looked back. Rust is lean and mean by default without this bloat.

Failures

In order for inheritor of a class to never inherit waste which is not needed the creator of a superclass must know of ALL the possible instances where and why inheritor will inherit his class. This is an impossible to predict lack of information. Even so, if the creator of the superclass knew all the usage, there is no way for creator of the superclass to write superclass perfect for every inheritor to perfectly adapt to his specific need.

REMEMBER FROM EARLIER : lack of information is unprofitable and specific solutions are superior to generic solutions

Encapsulation

What a fancy word which simply means you have private fields. There constantly arises a situation where you use library, or just other people's code and you have to access something private. Usually, people either just expose private variable (via getter or setter), edit the class which has private variable, or find some workaround, like recreating an object, changing a variable via reflection and so on. Just more useless code we need to fight and our codebase is in rebellion against itself and divided kingdoms cannot stand.

There are four possible cases regarding a class variable:

Variable is exposed Variable is private
We don't need the variable 1 2
We need to access the variable 3 4
  1. Variable is accessible, but we don't need it, no harm done.
  2. Variable is private, but we don't need it, so private qualifier has no value.
  3. Variable is accessible, and we actually need to change it, great, no need for workarounds and just operate on internals as you need. No need for extra code or headaches.
  4. Variable is private, and we need it to access it. This is horrible, and in my experience happens very often in an OOP codebase it's not even funny. Then the searches start, then the workarounds start, then surgeries on the class starts, state recreation starts and what not to achieve desired purpose.

I don't know about you, but I just don't go around looking for random private variables to change. If I can get something working in higher level I don't want to be looking in a lower level. I am not motivated as a coder to go around and change random private variables in random classes, neither I believe any sane coder is. And if I intend to change something inside I must know internals well enough to make those changes or I am not touching that with a 10 foot pole.

But, If I do need to change it, and I know sometimes that is the only logical and shortest way to accomplish this, the private barrier only causes more harm than good. In short, private qualifier loses value and makes your codebase paranoid and infighting with each other with these silly encapsulation games.

This is caused because the creator of the class with private variable never knew about the usage of the class which involves reading/changing the state of an internal variable. This is lack of information which caused this evil. The alternative is to simply leave everything public, and rely on the programmer, and that he does have enough information about the codebase to make the right choices. Out of all the languages that did not fail by introducing private qualifiers of the top of my head are python, lisp, lua, javascript (mostly scripting languages). Every single language with private qualifiers is guaranteed to have projects with extra code and workarounds just to get rid of a private qualifier restriction.

Failures

Author has lack of information regarding all usages of his class so he can never know what he can set private and do not cause needless friction for the users of his class.

REMEMBER FROM EARLIER : lack of information is unprofitable

Abstraction

This is ability to represent data without explicit details. Well, unfortunately, I've never designed a UML graph and it mapped 100% correctly to what it would be in code. Many people in the university I studied tried but I did not hear success stories from them either. Because, usually when you actually start to implement something, stuff pops up which you have not anticipated (removing your lack of information and changing your decisions).

So, abstract generic solutions in my experience never turn out the same in actual production and working code. This is caused by a lack of information from an abstraction creator, who does not know what the end code will look like and what will it do. Interfaces designed on board will probably need to be changed down the road (which is ironic, since interface ought to prevent change - not cause it). And another thing, abstraction is generic and it is inferior to the specific solution which is a real working code.

Failures

Author of abstract interface has lack of information on the final code and what it will do, so, he can never get the correct abstraction in a reasonably complex system from the start. Also, abstraction is generic and thus inferior to the specific solution.

REMEMBER FROM EARLIER : lack of information is unprofitable and specific solutions are superior to generic solutions

The conclusion

OOP dogma is plagued by two main evils on most categories which make it bad and inferior:

  • Lack of information
  • Preference to generic solution instead of specific solution

It should be clear by now that OOP is a bad idea and should be avoided like a plague. But what is the alternative? And I'll share with you how I code and how I think.

The alternative

Polymorphism

OCaml has enums, you can list all the finite values something will be and then simply match them at any code place to find the values you need and perform appropriate actions on them.

Lack of information is removed, because we know what all the enum values can be and we can go as specific as we want in any code place without any restrictions and negative effects on the enum itself (changing it's representation as we may need to change interface etc.)

Inheritance

Do not use inheritance. You can simply use structures and add as many fields as you want to make sure that all the fields will be used and there is no extraneous waste and construction going on.

Lack of information could be avoided by simply using specific structures tuned for a specific job at a specific place, instead of a single monster many field structure for everything. More specific structures for specific purpose are superior to single generic structure which is good for no single purpose.

Encapsulation

Just leave everything public and never worry about having to workaround certain limitations which are caused by lack of information from the struct creator part.

Even if the creator of the struct has lack of information about entire usage of a struct there are no friction points for the user of the struct because we control and may change anything.

And if bug is introduced by messing with internals that shouldn't have been touched in the first place, just fix the damn bug - much easier than working around private qualifiers :)

Abstraction

Use specific functions at specific points and change them as specifically as they need to be changed. Even though, we still have lack of information regarding the final picture of the system, we do not have any interfaces friction points which cause us trouble and extra work. Just change code where it needs to be changed, using functions and structures, write specific code which is the perfect fit of the situation and be happy.

Lack of information was not completely mitigated, but at least nothing gets in our way when we need to change stuff. And our code is now specific for the situation which is superior to generic solutions.

TL;DR

OOP sucks and should (probably) never be used. I've been coding my projects for at least 3 years without classes and methods and I've never thought "Man, I wish I had classes and inheritance", but in OOP code bases today, because we still have some of this rot in the work, I am constantly thinking "Man, I wish I could just write a function instead". I am yet to come across a problem that functions and structures with public fields wouldn't solve.

Use public functions and structures with public fields and live a happy and a long software developer life.

With love, Abner.