I’ve explored DHH’s way of writing Rails applications. His introduction of CurrentAttributes and suppressors of callbacks a few years ago made me want to revisit his Youtube screencasts on Basecamp 3 and try to really appreciate this approach with an open mind.
I soon understood our fundamental difference in thinking. DHH approaches application code as an interconnected web of rich models. Each model is chock-full of concerns (modules with generalized functionality). Each model’s methods produce ripple effects far and wide across associated models, via numerous callbacks spanning many modules. This approach is somewhat graph-like. You have a graph of rich nodes, and as you activate one, it activates other nodes at various distances in all directions.
This finally made me realize that with such a dynamic way of viewing application behavior, it makes sense why one might want to suppress entire classes and categories of callbacks. Those ripple effects are nearly untraceable, and require blanket suppressors from the top. Instead of directing logic, we’re constraining logic that would otherwise spread in all kinds of surprising ways.
This also explains why it’s so difficult to avoid globals in this world. You don’t want to impede your vast ripple effects with such minutia as passing the same data across associations over and over. It’s a waste of time.
I want to say that this is a highly unusual approach, but to be frank, any consistent thought-out approach is unusual in our industry. Thoughtful codebases are unusual. So yes, it’s unusual to have a well-established approach consistently applied to the entire codebase.
I know many people disagree with DHH, but I have not heard them provide a good alternative way of viewing the entire application. I’ve seen rebuttals to individual features, but not a challenge to this entire world view. Frankly, DHH himself didn’t really conceptualize his view either.
My biggest problem with this approach is that in his videos it was really hard for me to follow the story that his code is telling. Since all ripple effects are unapologetically triggered via chains of callbacks, it was difficult to follow so many paths into so many directions, ending up with so many outcomes. I find that this doesn’t work well with how I think.
My alternative way of building apps is narrative centric. Instead of creating a web of nodes with ripple effects, I want to create small stories, each living in an entry point (whether that’s a controller action, bg job, rake task, or test). Each story has a beginning (the initiating request/call) and an end (the response), with side effects in between. I love seeing a complete story where every major plot point is clearly visible at the controller level. If groups of plot points are often repeated together, like tropes, I love the ability to group them as needed in order to keep things DRY. I love that I can look at every entry point, and decide how best to optimize it. I can decide to put various actions into a transaction, or group multiple actions into one for a more efficient SQL call. Or introduce an exponential backoff retry for something. I find that with the narrative approach I have the most clarity and flexibility.
To write beautiful short stories, I prefer to focus my attention on gifting myself the domain language necessary to write them. Both Ruby and Rails allow for very expressive routines, as long as your business logic is neatly packed away beneath clean interfaces.
Top comments (0)