The Rails Doctrine
rubyonrails.org
rubyonrails.org
Anyone who is giving Rails a serious look, should take the time to read Eloquent Ruby. When Rails prioritizes programmer happiness, what Rails is really doing is prioritizing the happiness of the lone rockstar developer who is the sole writer, owner, and operator of that codebase. You can build some code that is, yes, quite eloquent. It will be a complete work of art (and yes, Eloquent Ruby was quite illuminating at how Ruby uniquely helps you to write code as art). It will read like English. Your productivity will be phenomenal. You will feel like you can cure cancer by yourself. But all that flies out the window once you need to share that code with someone else, or inherit a codebase from someone who thought of himself as Da Vinci and his codebase as a modern Sistine Chapel. Then it becomes a colossal pain. Because while the codebase itself might be art, the shared context for how to work within that art is nowhere to be found.
I’ve said it once and I’ll say it again: Rails is the least-scalable framework ever when it comes to # of engineers. Someone is surely going to respond “you can just add more instances!” Not what I’m talking about, though it’s bad in that department too.
what alternatives do you find are better when scaling up with more engineers?
Nobody understands the conventions so they cargo cult whatever they found from a blog that was last updated in 2013. Or they find novel and extraordinary solutions to problems that the framework literally already solved. Or they have zero experience modeling things in a database—much less doing it the way ActiveRecord encourages—so every battle is always uphill.
The biggest problem with convention over configuration is when you have a team where nobody understands the conventions. But what else do you expect?
I will happily concede, though, that I do think in that sort of situation that Rails can very easily become worse than other languages/frameworks.
Then of course you can find badly written code in any language, and if a quirky would-be rockstar creates a project thinking about art instead of readability and maintainability, they can definitely create an unmaintainable mess with Ruby on Rails - there is nothing preventing you from doing that.
As the number of developers on the project increases, said probability approaches 1 ;)
[0]: https://docs.ruby-lang.org/en/3.2/Method.html#method-i-sourc...
Rails is very opinionated, and when I've encountered new rails code bases they are easily legible to me and can readily be understood.
Even outside of rails, the largest real pitfalls of clever ruby I've seen are related to gratuitous use of metaprogramming, which one can sometimes encounter if you need to maintain or find a bug in a gem you aren't familiar with.
Maybe your problems weren't with rails, but with the programmers of the code bases you were looking at? I believe it's possible to write unmaintainable code in any language.
There's lots of old, broken shit in the Ruby ecosystem and stuff is constantly breaking because of churn and the lack of meaningful, accessible gradual/static typing. (Elixir roughly in the same boats above.) It also doesn't help that some of the gatekeepers of Rails/Ruby have megalomaniac, reactive personalities who make interacting with them sheer misery and CWOT.
If you want what looks like Ruby compiled, there's Crystal. Go and Rust backend frameworks exist also.
Maybe I've been just lucky in the at max 20 or 30 larger projects I worked on so far.
With the added pain that "me today" and "me two years ago" are often different people too.
Rails shows this exact same collosal pain over longer period of time.
Rails optimizes my happiness today by trading it in for my happiness in a few years.
You’re right, it’s easy to bikeshed on which syntax constructs are more elegant, but the Ruby ecosystem provides tools to eliminate the need for that conversation.
This is the solution I've gravitated to in other languages (e.g. Black, gofmt), but that was after my time in the Ruby ecosystem.
"foo bar"
.split(" ")
.map(&:capitalize)
.join " "
There is really no advantage to not putting parens for the `join` call. Meanwhile, it makes modification a pain as well as makes for some ugly diffs.But as a sibling comment mentioned: just use a linter.
# Does not work
"foo bar"
.split " "
.join " "
There was a proposal to introduce a "pipe" operator at one point which was shot down [0]. It wasn't actually a pipe operator at all and really just an operator that would have made the previous syntax possible (|>, instead of .)[0] https://dev.to/baweaver/ruby-2-7-the-pipeline-operator-1b2d
For calls with some non-block arguments, it's not so clear-cut as "always include parentheses".
Some methods are so prevalent that they almost become part of the language "syntax", even though there's nothing syntactically special about them. For example, you rarely see parentheses used on calls to `attr_accessor` or `private`:
class Cat
attr_accessor :fluffiness
private def plan_world_domination
# TODO
end
end
I think parentheses around `private(def some_method ... end)` would be especially non-idiomatic. Even though passing the result of the `def` expression to the `private` method is exactly what's happening.Another place where it's usual to omit method call parentheses is on embedded DSLs. For example, in RSpec it's uncommon to include parentheses on calls to `describe` or `it`.
describe "RSpec DSL" do
it "looks neater without parentheses" do
# ...
end
endYes, that's the extra decision step in my main workflow loop that I'd like to cut out whenever possible.
Mostly I was thinking about stuff like arrangements of hashes, arrays, method arguments, naming of methods and variables, various styles of method chaining, block usage, etc.
I love ruby and find it and I know Rails also has some magic, but I love my types. I'm glad I'm going back to do something sensible in a few months. Probably Kotlin + http4k.
That includes using frameworks like Active Storage, features like CurrentAttributes or defaults like Minitest.
Some of it is controversial or not favoured by community. Not many people experience Rails with the core value it's designed around which is a shame!
It's great you can replace certain parts, but should you?
6. Rails Cheatsheet
7. The Rails Doctrine
Interesting coincidence. Right on top of each other too.
Rust/Go/Elixir/Gleam/Kotlin/Clojure/Crystal/TypeScript are all infinitely better, in my experience.
There are so many options now, you just need to explore them and go outside your comfort zone.
If I ever did use Ruby again, I would use Jets over Rails.
Though I still do some Ruby. A quick Sinatra proof of concept, some Jekyll hacking or the occasional day of spelunking an old Rails app that suddenly broke for some reason.
But doing most work in Rust, Golang and typescript shows me how much Rails is a pias (but how beautiful Ruby is, and that beauty isn't a great trait for a production programming language)
I've since moved on to Elixir's Phoenix. Not quite as opinionated as Rails but many of the low stakes decisions have been made.
If you want to understand the core values of Rails they are:
* Implicit behavior through conventions regarding core features like databases and routing * The framework has a reasonable built-in for anything you'd need to make a web app * Major new features or changes usually originate from DHH and 37 Signals
The stated values are mostly useless:
* Programmer happiness - completely subjective as to who is being made happy. Lots of Rails things make lots of people unhappy just as much as the opposite.
* Convention over Configuration - this is the only clearly stated value and it is basically the core ethos of Rails, even though it really means "as compared to J2EE". Rails has a TON of configuration you still need to do on any project.
* The menu is omakase - the text explains better, but the point is that everyone should use the selected tools and yet a good chunk of Rails developers deviate from stuff like minitest, fixtures, etc. That the defaults can be swapped out was never a goal of DHH's and I think they exist only because of the merge with Merb way back when.
* No one paradigm - this is just a retcon for how different parts of rails are different from no really good reason. Why are helpers a big blob of global functions? Why does a controller expose data to the view via instance variables? Why do schema migrations default to nullable fields and no foreign key constraints? Why do you define instance methods on mailers but invoke them as class methods? There's no rhyme or reason to this. It's not bad, but it's also not the result of deeply thinking about the best way to approach these problems.
* Exalt Beautiful code - DHH sets the standard of beauty. This is possibly the worst part of the doctrine because when others adopt this it leads to constant infighting about style and other pointless things. If you've ever seen any of Basecamp's code online, I would not personally call it beautiful. As a doctrine for an open-source project this is useless.
* Provide sharp knives - this is a curious one both in text and analogy. What he's saying is that "dangerous" features should be OK because people should be trusted to use them. Fine, I guess, but if you do any home cooking, sharp knives are far more safe than dull knives. And any language feature can be abused. What is very frustrating about Rails is these "sharp knives" tend to be unobservable so it's hard to know what is happening and why.
* Value integrated systems - this is where it is either DHH's lack of experience or just flat out delusion, because Rails provides exactly nothing to help scale a monolithic system to hundreds or thousands of developers and tons of performance requirements. It can be done - look at Shopify and GitHub - but nothing about Rails gives you a leg up on what is required to build a monolithic system. THAT SAID, like configuration over convention, this does demonstrate Rails value in a roundabout way - you get mostly what you need in one package and, unlike the JS ecosystem, don't have to piece it together on your with starter packs and boilerplates
* Progress over stability - Maybe initially this rang true, but Rails has been extremely stable for a long time, and that's a good thing. The team does a great job of deprecating things and providing an upgrade path, and Rails happy path features very rarely break between versions. But what progress has Rails made at a high level? It still works more or less as it did when it came out. This is totally fine and should be heavily promoted as an alternative to the constant change, rot, and churn in the JS ecosystem.
* Push up a big tent - this is almost entirely untrue. DHH does not value this. Maybe he did when this was written, but he does not value diversity on any real level, at least not based on his written words and public actions. This includes "diversity of thought" as well as "diversity of people".
The Doctrine is a nice idea, but I don't see how it helps anyone really understand Rails beyond "DHH's framework". And again, that's fine. It's his framework. He doesn't have to explain himself and, to be honest, I kinda wish he wouldn't.
That a framework built on a programming language no one even knew existed would become the most effective web development platform for the better part of the next 5 years was beyond anyone's expectations. It's because Rails was built on consistently good or great decisions, and the only way to be that consistently good is to have a doctrine guiding you.
From the outside it might look quirky, but having a strong opinion on how things should be done specifically for developer productivity and happiness has been a corner stone of the Ruby community since its inception.
point is, success doesn’t necessary mean a coherence of thought and practice. especially in rails’ case where we’re talking about its popularity among humans, the most irrational of beings. the crowd elected rails, not for its doctrines. i’m a rails developer and contributor, and none of that arrived by virtue of the doctrine dhh (and by extension, you) espouse.