Inside the Dev Team Death Spiral
It’s not dogma, some theoretical belief that my way is THE ONE WAY. If anything, it’s the counterpoint to the popularly accepted notion that `class` is just another tool, and it’s all in how you wield it.
The truth is, some tools are better than others, and when something much better comes along, we stop using old tools.
Well, sometimes we stop. Unless dogma is holding us back. I think that dogma sucks the life out of programming:
Why Do I Keep Going On About This?
I advise people to avoid class because it causes real problems in real projects, and that impacts real lives. I am an entrepreneur. I have gone all-in on several projects, only to see them fail. This is not hyperbole, this is a fact:
A failed startup can leave people penniless, jobless, and up to their eyeballs in debt. I’ve been there. I’ve seen it. And today I’m sharing it with you, so when I tell you that `class` inheritance can wreck lives, you will know…
I’m not pointing at some imaginary devil and shouting “evil, evil!” I’m telling you what happened to me. What happened to my friends. What happened to the CEO I was loyal to in the beginning of my career.
I’m relating real experiences that I have been involved in. Stories featuring real people whom I knew well and worked closely with. I wouldn’t insult those people and thousands of others who have been affected by these problems just to score a few points arguing with anonymous people on the internet.
I don’t think that people who support `class` are stupid, either, and if you’re one of those who have seen what I’m talking about, it’s not useful to put other people down just because they disagree with you.
No, they’re not stupid. Yes, some of them have built “real production apps.” The fact is, you can build real, high-quality production apps with classes and without them. It’s just less work without them, and I’ll tell you why if you keep reading.
Some really smart people worked on the `class` specification, and many more really smart people are using it, writing about it, and encouraging other people to use it.
But sometimes really smart people are unaware of the full scope of the situation, or unable to see better alternatives… like all the people who continued to insist that assembly language was the best way to write high-quality software well into the 90's — 40 years after very good high level languages become available.
So Why Argue?
I do this because maybe somebody new, or somebody experienced & still open minded will run across these warnings and realize, “this guy has been where I am. He’s been down both paths. He knows where both paths lead, and a whole lot of people echo his advice… maybe there’s something to this.”
I don’t want to win a debate on the internet. Believe it or not, I don’t care about that. This is about you. If you see this warning sign and realize there’s a cliff ahead… If your project succeeds instead of fails, that’s a win.
I want you to succeed.
I’ll leave you with two short stories, and some commentary I’ve seen pouring in today:
A few years ago, I was working on a really brilliant team building a really cool product. We were working with the biggest names in the industry, and our app had tens of millions of users, and somewhere in the neighborhood of 100k lines of code.
One day, something great happened. We heard about Backbone.js. This is before we all had great working modules in JS (AMD was brand new), so the organization that Backbone brought to our code was a wonderful thing. I loved it, and that `.extend()` feature. I knew what that meant, but as long as we were careful it would be OK…
Until one of our coworkers went on an inheritance binge. This thing inherits from that, and another inherits from that and on and on… One day I found myself working on a bug in a leaf component six layers deep in a class hierarchy. Each component ran some initialization code to fire up event listeners and so on. So I stepped through the code one line at a time up the `super` constructor tree, checking every value of every variable, trying to figure out where things had gone off the rails.
After way too much time doing that, I found the problem in the base class, and there was absolutely no way to work around it without changing the base class. But lots of widgets relied on this particular base class. When I fixed the problem, they all broke.
I had no choice but to fix them all. Hours of frustration that should have been a five minute fix. This problem is so common, it has a well-known name: “The Fragile Base Class Problem.”
Three months later we had various modules providing services for the whole app, so we started using a dependency injection container that abstracted the instantiation details of our modules to give us a uniform service instantiation interface — similar to the DI container in Angular which has four different ways to create a service. It wasn’t OK after all.
Rewind. A long time ago, before SaaS was coined, I worked for a company building educational software. Building it was a breeze. There were a series of modules that you could inherit from, each of them would create a different educational experience for the user, and they all inherited from a base class to provide the module interface.
Just one problem: We were building wildly different educational apps for wildly different industries, each important because they were serving contracts that were keeping the lights on.
But new modules brought new use cases that nobody thought about at class design time. These new use cases required changes to the base class to make them work — but dozens of modules depended on that base class, and the various classes between that base class and the new module class.
I can almost hear you thinking…“But that’s stupid. Everybody knows you should keep your inheritance trees shallow and use composition. My team would never do that.”
Yes, I see you, Twitter. I know you’re on a brilliant team who all know the right way to do things. But class hierarchies have a tendency to grow when you’re not looking. If you’re not ever vigilant, the new guy on the other side of the room or the other side of the country is busy extending that class you never intended for extension.
Because `class` affords `extends`, like chairs afford sitting, and balls afford throwing. That `extends` keyword is right there, tempting the busy programmer who’s facing a tight deadline and pressure to close this ticket yesterday.
“I know this is wrong, but just this once… I’ll fix it tomorrow.”
But tomorrow you have another deadline, and you forget.
Class hierarchies creep like the crack in your windshield you were going to fix 2 weeks ago, or that mold under the faucet you don’t even see. They dig their hooks in by requiring users to instantiate them with the `new` keyword. Get enough clients using `new`, and you can’t back out of the constructor implementation even if you want to, because code you don’t own will break if you try.
So we extended. And new use cases came. And the code grew more fragile. And the bugs piled up. And when we fixed one, 99 more would appear like magic. And we kept missing our promised ship dates. It’s a death spiral.
One day I was working at my desk on a plugin, when two sheriffs and a young guy in a suit whisked by. They barged into the CEO’s office. The door shut loudly. 5 minutes later, the boss came out and told us:
“This is a hostile takeover. The investors are firing me, and they’re going to sell the company I built over the last 25 years. The company that feeds my family. And your families. They’re taking it away from us!”
And they did. I watched as they laid off everybody working on new development projects. I watched as they shut down everything and milked the aging cash cow that was keeping the company afloat. One day they laid me off, too. I thought my job was safe. I was wrong. After 3 or 4 waves of layoffs, it was finally my turn.
And then the business vanished like it never existed.
I never saw that CEO again.
I’ll leave you with some of the recent discussions on this topic:
I would never argue that ES6 classes are “clearly a better choice than composition, modules, or…
ES6 classes have a few benefits:
And my rebuttal:
We don’t need a standard for single inheritance. Single inheritance taxonomies are an anti-pattern.
It could be composition all the way down, using a variety of very simple techniques in factory functions.
How to Use Classes and Sleep at Night
Justin Lowery (@cerebralideas) | Twitter
The latest Tweets from Justin Lowery (@cerebralideas). JS application engineer; UI architect; amatuer dancer…
The Last Word
For me, the bottom line about `class` is simple: It’s not enough if you think `class` is manageable. In order to justify the cost and the risks, there must be a really compelling use-case for class. AFAIK, there aren’t any.
“If a feature is sometimes dangerous,
and there is a better option, then
always use the better option.”
~ Douglas Crockford
He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.