Why we must stop listening to DHH and embrace FP

I loved Rails. I owe the 2nd through 5th years of my career to it’s popularity. Although, I supposed my clients would’ve gone with what tech I wanted to choose. Rails was exciting in 2006. It was definitely the cutting edge. This was pre 2.0. Pre REST. Finally we had a common project structure and some great syntax to handle relationships in databases. He did create a very quick blog using generators. Generators were a huge part of why I chose Rails. And Ruby seemed fine. Cuter than PHP and Java at least. And it was easy to learn. The only real hard time I ever had early on was with block scope. But once I learned that idiom, we were off to the races.

And DHH lent a lot to the picture. He was young and energetic. He personally worked with the Textmate guy, to choose a freaking font for working in the framework. His talks made sense to me. Iterate fast, break things, test and only write what your users need. They were confident, they build a movement of other incredible thought leaders innovating today. I have no intention of minimizing that legacy. But DHH is wrong now, and we all have to stop listening to his old-man non-sense just because we’re mid-career and don’t want to learn a new paradigm. The old paradigms are not workable at scale.

The Middle

Building a simple blog (or these days a chat app) in 15 minutes is great for adoption and learning and understanding in the beginning. But the beginning doesn’t last forever. On apps with any tracking, the majority of the time we’re working on a project is the middle. The middle is about building new things sure, but most of the time, it’s about updating, changing, and removing the current feature set in response to usage and feedback. The middle is where things start to slow down. The middle is way harder to learn about and understand in the beginning.

The middle brings lessons of cloogy code, where a developer added code without disturbing the pre-existing stuff, or of patterns from another language that leave the project feeling messy. The middle brings the need for testing, which brings mocking or fixtures. It brings even more abstractions that don’t really belong to an object in the real world. But most importantly, the middle brings more co-developers, working together on the same code, changing each other’s objects in questionable scopes. And we start to slow down. This inefficiency seems to be tied to the size of the project, instead of what the slow down really has to do with. The State and scope of the data.

Objects

Ruby felt different because it was. Everyone loved that it was objects all the way down. Objects were and still are the fundamental way developers learn patterns to solve web problems. They seem easy to reason about and, in the early days of a project, they are. But objects have their drawbacks as a codebase gets larger, as I’ve learned over the years. State has become an important topic. Mutability is another, testability, that one was a surprise. These are old concepts that engineers talk about when they’re trying to explain to a developer why their app is so hard to reason about and thus so hard to refactor or update. I think what the engineers are trying to say is,

“Maybe objects aren’t the best way to conceptualize a User-to-DataStore, pipeline of information service. Eh?”

Objects may be more suited to conceptualizing an internet of things or a virtual world in a video game. But even then, the Ruby language sets one very important default wrongly. Imagine a world where a class in Ruby automatically sets all methods to private and you had to explicitly write public to make a method public. These “getter/setter” methods are what the original architects of OOP had in mind. They didn’t want objects reaching into each other’s internal state. They wanted independent “actors” sending each other messages. But if the language doesn’t enforce the rules, they are broken and when the problem is the language (or the paradigm itself) it will undo all other efficiencies. As the king of virtual worlds said in 2013.

“Everything that’s syntactically legal will eventually wind up in your code base.” — John Carmack

And maybe in the case of persistent multi-object 3d worlds, instances and instance variables have a place. But the web is really nothing like that. The web is a place where we input a URL and get a website, persisting no instance variables. Every request sends the full payload of input and every response, the full headers and body needed to render in a browser. It’s encapsulation and decentralizations is what makes it great.

DHH loves integrated systems. You know who else loved integrated systems?

This fucking guy. Remember this guy?

Functional

So I say the architecture of a website should mimic that of it’s delivery system. I say state is temporary and best passed via an immutable object from function to function rather than kept on an instance of an object that contains it’s own functions which can be called from other objects. The function is the central actor here, not the object. One might call this functionally-oriented programming, the other objectional programming.

The function is the unit all these words are talking about. Self contained scope, composable and declarative for better communication between developers. Ok, by now you’re probably thinking. I got it, you like functional. I’ll stick with what I like, you stick with what you like. Along the lines of this fellow. I hear that, but if I have to work with your code or work with you I’m going to want to communicate effectively inside the codebase. Because, as we all know, documentation is technical debt.

Communication in Code

I think what we want, is to be able to understand the flow of a program by reading the code. And sometimes we can. Those times are amazing and make us feel very productive. When we can’t, we want to blame the developer who wrote it. Why couldn’t that person take the time to comment every function and make the names as declarative as possible. We spend so much time communicating with the computer, we forget the computer will forgive extra declarations, extra code that encapsulates some piece of logic or turns a jangly if else if else if into a tight switch/case statement.

If time is money, then the most money is lost at the most critical time when not taking the time to communicate well in your code. This is always a struggle between the stakeholders and the meticulous developers. But even developers sometimes just want to be done with it. This is important because code must change and adapt and become more what the end user is looking for. More useful. As we dive back into code we wrote only weeks before, we sometimes struggle to read and understand why the code works the way it does and more importantly, how to insert the new functionality into the old object. It’s not called objectionality after all. How an object behaves can change so drastically in the early months of an application that their need for a new name becomes comically apparent. Functional programming suffers from namespacing too, but at least the namespace is just that, a namespace and doesn’t have a whole bunch of properties to keep track of as well. The composability of functions and using state externally to the functionality (the separation of code form data) lends itself much better to gradual improvement of a codebase.

Conclusion: Integrated Vs Distributed

There is no way in hell I’m wrong that the internet is not integrated. The internet is distributed and uses standards to work cooperatively, but it is not integrated. If our apps want to grow and change with this massive distributed system we must use tools made for multi-threaded distributed systems. The old purely OO languages are not fit for the task. Functional languages like Elixir and Elm and their Javascript counterparts will be the future of the web because they mimic the pipeline all the way from the user’s input to the browser’s output.

Further Reading: Elixir and Elm