Object-Oriented Programming is an exercise in trust

Published Nov 9, 2020

Every time I start working regularly in a new programming language, I spend the first few months or so absolutely hating it. It has absurd conventions that make my code ugly. It doesn’t let me think about my program the way I want to. It’s missing some fundamental building block that I keep needing to emulate.

And then, after living in that ecosystem for a while, it starts to look different. What’s actually happening is that I’ve learned some of the idioms, learned the new patterns, and started to think differently. There’s clear evidence here. Usually the compiler/interpreter, language version, and libraries haven’t changed at all!

I went through this with Python (my very first language, which makes it the hardest), JavaScript, and Go. Now I’m going through it with Ruby. I’m currently at the, “ah, I see that we value different things” step.

As you can tell by my language background, I’m really not well-steeped in object-oriented programming (“OOP”, or even shorter, “OO”). Objects in all three of Python, JS, and Go mostly follow the C pattern of struct-plus-functions. There’s a clear separation between accessing the data and behavior of the object, and you have to do extra work if you want to blur that line. Python has the @property decorator, JS has getters and setters, and Go actually doesn’t let you do this at all (which is actually more frustrating).

Ruby’s line is intentionally blurry by default. One of the first things you learn is that attr_accessor, attr_reader, and attr_writer make it easy to define the getter/setter pair for an instance variable. So it’s expected that you’ll define those methods and use them as part of your public and private API. What that means to me is that when I’m looking at some Ruby code, there’s no visible distinction between obj.name being a “simple” attribute reader or being a “full” method call with complex behavior.

This isn’t a bad thing, it’s just different than what I’m used to. For one, it means that there’s no “just data” objects. They’re actually discouraged, if I’m hearing the OO advocates correctly. In theory, basic data types are “just data”, but you can put complex behavior on an Integer if you want to monkey-patch it (and we do!).

So every method invocation could have complex behavior, which brings me to the main point: reading and writing Ruby requires a level of trust that those other languages don’t. Since I can’t possibly read all the code I’m relying on, I sometimes have to trust that some object that I’m holding won’t change out from under me. And that scares me!

I know that this is the whole message-passing thing: thinking of objects as performing services in response to receiving messages. But that feels like a lot of unnecessary trust when most of the time I just want the object to hand me the data it has so I can make decisions or send it somewhere else. Maybe this feeling points to “bad” OO software design, or maybe I don’t have enough experience to see how this trust requirement is actually beneficial to me. I’ll give it a few more months.