This post is based on my talk “Desorientados a objetos” (in Spanish), which I gave at Beyond Tech Notes and at Codemotion Madrid 2017.
We go to conferences to hear about using Kafka to keep the state of our applications, and then we land on projects that fail at the basics. This post is half talk, half rant about one of those basics: object orientation. Not from an academic point of view, but from what I keep seeing in day-to-day projects, where we use an object-oriented language and believe that makes us object-oriented.
The chain was intact
For many years I lived in a village close to the countryside, and we liked to go mushroom picking. An acquaintance of mine does it for a living: he spends the day picking mushrooms and then sells them to restaurants.
One day he drove his van to the Iruelas valley, a nature reserve in Ávila. The rest of us leave the car by the road and walk up the hill, but he saw that the chain blocking the track was down, so he drove in. When he came back after a long day, the chain was up again.
My first thought would have been: you’re from a village, you have a van, there’s a hoe in the back, hit the padlock, and if you have any dignity, drive down to the village and offer to replace it. But this man has peculiar principles. He believes in the energy of pyramids and similar things, and breaking a chain would break a flow of energy. So he looked for another solution.
He spent hours with the hoe digging a way around the chain. The local paper ran the photo, and the chain was intact.
He was caught and fined, and he still didn’t get out, because the gear lever broke while he was trying to drive the van out of the hole.
It happens to us all the time. We get the base of our reasoning wrong, the principles behind our decisions, and those wrong decisions turn into practices: eight hours with a hoe to get the van out, when maybe there wasn’t even a wall on the other side.
One class, 2,300 lines
Not long ago I worked on one of those microservices projects that are so fashionable now. I couldn’t resist pulling a few useless metrics:
- 2,300 lines of code
- 344 public members
- 205 properties
- 139 methods
In a single class.
You could forgive the 205 properties. Designing aggregates is hard, someone got carried away. But the 139 methods weren’t business behavior. This was a .NET project, and in .NET you don’t need a GetName() method when you have a Name property. Whoever wrote it had heard that anemic models are bad and that public getters and setters are a bad practice. So they made 205 properties with private setters and 139 public methods to set them. Well, 138, one of them was the constructor.
Knowing the people involved, their intentions were the best. They held on to a principle they had misunderstood, and who hasn’t? The trouble is that once a class has 2,300 lines, changing it is hard and often not worth the money. The project was in production and the customer was happy. Customers don’t care about zero coupling and infinite cohesion. They care that deployments don’t break and that their boss doesn’t come after them. At that point the most you can do is apply the boy scout rule.
When I said we weren’t really doing object orientation there, the obvious question came back: so what is object orientation?
What is object orientation, anyway?
We’ve spent decades hearing about object orientation, and it’s still hard to find a consensus. If each of us wrote down a definition, there would be common ground, and plenty of “that’s not it for me”.
Formal education usually reduces it to a mantra: polymorphism, encapsulation and inheritance. Then you start reading and find Alan Kay, one of the first to use the term, saying that for him it means messaging, local retention and protection of state, and extreme late binding. None of the three words in the mantra. Bjarne Stroustrup sold us a different idea with C++, and with Java I won’t even start.
In 1995, Meilir Page-Jones told the story of locking a group of object-orientation experts in a room until they agreed on its essential properties. They only agreed on one: encapsulation.

How I picture the experts by the time they agreed.
Encapsulation means bringing together functionality and data in a cohesive way. For me that is the essence, and it fits Kay’s definition too: objects take center stage, and what matters is the messages they send each other.
I wrote about these definitions in more detail in Rediscovering object-oriented programming and Encapsulation, visibility and state retention.
How much damage we’ve done
Tools and techniques aren’t bad. What’s bad is the way we use them. Each of these was a missed opportunity to model our software in an object-oriented way.
The “enterprise” style. There was a period when the need for software exploded. On one side came Visual Basic, which made it all about dragging boxes onto a form. On the other came Java, with an interpretation of object orientation built on inheritance that did a lot of damage. They have paid many bills, and there are good products built with both. The blame isn’t on the languages, it’s on how we used them.
Code generators. We say object orientation means keeping state and behavior together, and then we use tools that turn the state into a class with getters and setters, and send the behavior somewhere else. You drew your state in a UML or UML-like diagram, gave a name to some behavior, and that name became a stub to fill in. State on one side, behavior on the other, and you were sold.
Stored procedures. If objects are data and behavior held together cohesively, taking the business logic to the database goes completely against object orientation. We do it because we assume the database owns the state. It persists it, so it must own it. And if it owns the state, the behavior should be close to it.
ORMs. Again, our use of them. We handed our state to the database. It’s like thinking that the money you put in the bank isn’t yours. Ideally it’s still yours, and you only delegate its persistence to the bank. That’s what ORMs are supposed to help with, so we can forget the ceremony around persistence. Instead we let them into every corner of the code, and sometimes they force us to model things differently to keep them happy.
The owner of the state should be our objects.
Behind all of this are companies that think making software is like making churros. If that’s what you want, take Visual Basic or the tool of the day and make lots of churros. They’ll be great. But it’s something different.
Object orientation is just a style
Using an object-oriented language isn’t doing object-oriented programming. Object orientation is a programming style, and it’s one option among many, not the only one and not the best. There’s a lot of software written in COBOL, I assure you it isn’t object-oriented, and that’s fine.
The problem is that using an object-oriented language makes us feel we work in an object-oriented way, and admitting otherwise creates cognitive dissonance. Like the guy who says he takes good care of himself and smokes like a chimney, but goes to the gym more.
It’s often a matter of expectations. We picture our model as strong and robust, everything well thought out, ready to stand the test of time. Then we look closely and it’s Christian Bale in The Machinist. I can’t think of a better picture of an anemic model. It’s fine to get it wrong, but let’s be realistic about it.

Expectations and reality.
Anemic models and services everywhere
An anemic model is one where all we have is state. Other classes come along and handle that state however they like. Take state plus behavior, pull the behavior out, and what’s left is your anemic model. It’s a DTO that you call a domain model. From there it’s an endless road of pain: everybody touches your state, and you no longer know whether it’s valid.
This connects with something I see a lot: mixing up DDD and object orientation. I’ve read that the technical part of DDD is just object orientation done right. Everything is related, since applying DDD in an object-oriented language means taking care of your object orientation. But someone says “DDD”, you hear services, entities, aggregates, and you stay with the words. Suddenly there are services everywhere, and the whole domain lives in them on top of an anemic model.

Services, services everywhere.
Even a well-protected class can end up like this. The Box validates its own state, and then the one piece of behavior it has goes to a service:
class Box
{
public int Height { get; private set; }
public int Width { get; private set; }
public Box(int height, int width)
{
if (height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(height));
}
if (width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(width));
}
Height = height;
Width = width;
}
}
class AreaCalculatorService
{
public int Area(Box box)
{
return box.Height * box.Width;
}
}
The area belongs to the box:
class Box
{
// Same properties and constructor as before
public int Area()
{
return Height * Width;
}
}
In one project we had a class called ServiceService, which managed ambulance services. I’ve also seen a ManagersManager. At some point we could just use a name generator that appends “Manager”.
Poisoned patterns
Design patterns are important, and I’m not kamikaze enough to rant against them. But someone said that a pattern is often just a gap in the language you’re using.
Some fifteen years ago I worked on a project for a bank. At a bank you write little code and a lot of documents, and one of them was a design document. Our first version came back with a “what the hell is this?”. The technical lead had applied his own version of “nobody ever got fired for buying IBM”: nobody ever gets fired for approving a design document that only talks about patterns. He wanted us to write that we would use a facade, then a message bus, and call everything through a factory. Bullshit.
In that 2,300-line project I found the biggest concentration of strategies I’ve ever seen. I searched for *Strategy and got something like hundreds of them. Most could have gone away by modeling the domain properly and using the language. An if is a strategy too. Maybe your language has pattern matching. Maybe instead of a Customer with an IsDeregistered flag you model a DeregisteredCustomer object. Maybe polymorphism is enough.
This is the kind of code I mean. A type flag on the payment, and a strategy that has to look at it to decide what to do:
enum PaymentType
{
PrePayment,
Expense,
}
class Payment
{
public Payment(PaymentType type, decimal quantity) { ... }
public decimal Quantity { get; private set; }
public PaymentType Type { get; private set; }
}
class PaymentWhateverStrategy
{
...
public void DoWhatever(Payment payment) { ... }
}
Modeling each kind of payment as its own type gives the behavior back to the payment, and polymorphism does the rest:
interface Payment
{
decimal Quantity { get; }
void DoWhatever();
}
class PrePayment : Payment
{
public PrePayment(decimal quantity) { ... }
public void DoWhatever() { ... }
public decimal Quantity { get; private set; }
}
class Expense : Payment
{
public Expense(decimal quantity) { ... }
public void DoWhatever() { ... }
public decimal Quantity { get; private set; }
}
Patterns are fine, but they bring cognitive load. You have to know the pattern, or have someone explain it, and understand how it was implemented in your context, because not everybody implements them the same way. People reach for patterns because they look good, when what they need is to know their language better.
Null checks
There’s a classic picture comparing 0 and null: a toilet paper holder with an empty roll, and one with no roll at all. Most of our code is full of checks for the second case.

0 on the left, null on the right.
In Smalltalk everything is an object, so even the absence of an object is an object, called nil, and you can send it messages. We can borrow the idea and model absence as an object that knows how to behave, instead of asking != null everywhere.
public interface Logger
{
void Log(string message);
}
public class AnyProgram
{
private Logger logger;
public AnyProgram(Logger logger)
{
this.logger = logger;
}
public void DoAnyStuff()
{
if (logger != null)
{
logger.Log("Doing any stuff!!");
}
}
}
With an object that represents having no logger, the check goes away:
public class NoLogger : Logger
{
public void Log(string message) { }
}
public class AnyProgram
{
private Logger logger;
public AnyProgram(Logger logger)
{
this.logger = logger;
}
public void DoAnyStuff()
{
logger.Log("Doing any stuff!!");
}
}
Another example is navigating a tree, where we move between child and parent nodes. The root has no parent, and the leaves have no children. Instead of checking for null at every step, a root whose parent is an object that knows it’s the end of the road keeps the navigation code free of special cases.
Inheritance
Inheritance means many things. Between Halbert and O’Brien in 1987, Bertrand Meyer in 1988 and Timothy Budd in 1997, the literature lists many kinds of inheritance:
- Specialization
- Specification
- Construction
- Generalization
- Extension
- Limitation
- Variance
- Combination
Halbert and O’Brien’s paper, Using Types and Inheritance in Object-Oriented Programming, puts it in terms of modeling entities in the application domain. And specification is really a contract.
Despite the long tradition that treats inheritance as a cornerstone of object orientation, for me it’s one more tool. Some languages don’t even implement it as such. I much prefer composition.
Think of the cone of uncertainty. When we face a problem, our uncertainty is at its highest, and that’s precisely when we tend to make the most important decisions. Inheritance decisions made at that point often end up growing in a completely different direction, and then you have to live with them. Many times we reach for inheritance when all we want is three properties that another object already has, so we don’t repeat code.
Composition over inheritance
When we use inheritance, we make our design more rigid. A change in the class we inherit from exposes behavior outside our control.
With composition we buy weaker coupling, which reduces the cost of undoing.
With inheritance we couple to the implementation. With composition through interfaces, we couple to the contract.
An order doesn’t need to inherit from anything to calculate its price. It’s composed of the pieces that know how:
public class Order
{
private readonly ShippingCharges shippingCharges;
private readonly decimal price;
private readonly Discount discount;
private readonly Address deliveryAddress;
public Order(...) { ... }
public decimal CalculateTotalPrice()
{
return price - discount.Calculate() + shippingCharges.CalculateFor(deliveryAddress);
}
}
Small interfaces also let us combine behavior without forcing a hierarchy. Not every dog barks or bites, and a sea lion barks without being a dog:
public interface Dog
{
string Breed { get; }
}
public interface Barker
{
void Bark();
}
public interface Biter
{
void Bite();
}
public class StandardDog : Dog, Barker, Biter { ... }
public class ToothlessDog : Dog, Barker { ... }
public class ToothlessMuteDog : Dog { ... }
public class SeaLion : Barker, Biter
{
public void Bark() { }
public void Bite() { }
}
What I keep in mind
None of this needs to be academic. What matters to me day to day are things I can explain quickly and that help me notice smells early. Explaining them in depth can come later.
Principles first, then practices. We’re quicker to adopt practices than to understand where they come from and what problem they solve. With something as established as object orientation, we take a lot for granted, and that’s when we get into trouble.
The language shapes the way you work. In Practical Object-Oriented Design in Ruby, Sandi Metz recommends not exposing even a read-only field directly, but wrapping it in a method. If you later learn more about the business and need to calculate that value, you save yourself refactoring pain. That makes sense in Ruby. In C#, a property is already that indirection: inside, it’s a get method. Making it private and writing a GetName() method because Sandi Metz said so in a Ruby book misses the context.
Keep ORMs out of your object model. I try to avoid ORMs, but they have their place and they save you some SQL. Although if it’s just CRUD, writing four silly SQL statements won’t kill you either. I want them isolated in repositories, so that if I change the ORM tomorrow my model isn’t infested with its bits. This gets worse with ORMs on steroids, with LINQ and the like. People end up querying the database from a controller without noticing, because the class isn’t called Repository.
Borrow from functional style. You don’t need to be rigid. Immutability works well in object-oriented code, and in DDD value objects should be immutable anyway. Not long ago someone asked me if I didn’t feel dirty for giving part of an application a functional touch. I don’t. Sometimes it’s a good way to move behavior that doesn’t belong to an object somewhere else, without creating a class called Helper.
Coupling and connascence. You can’t talk about object orientation without talking about coupling. Page-Jones’s book introduces connascence, which is well worth knowing.
Think about the cost of change. Not every project is greenfield. When the fish is already sold, you don’t need to rewrite everything into “real” object orientation. If your model is anemic and the business logic lives in services, but it works and the customer is happy, leave it. If the project has a future, the cost of change will pay off through the boy scout rule. Economics should guide these decisions, especially in code you inherit.
Object orientation is just a style. It’s one option, and choosing it is a decision, not something that comes with the language.
Simple design by default. No designs and architectures “by default”. By default, no ORMs and things like that.
As the old saying goes, “FORTRAN programs can be written in any language”. So can procedural ones, and an object-oriented language won’t save us from that.
Further reading
There’s plenty of literature on object orientation. Two I keep coming back to:
- Meilir Page-Jones, What Every Programmer Should Know About Object-Oriented Design (1995). It gets tedious from the middle on, when it introduces a UML-like notation, but it’s where connascence comes from.
- Sandi Metz, Practical Object-Oriented Design in Ruby. Very good, as long as you remember it’s about Ruby.