For what it's worth, the last few years our we've sliced off a few custom client apps in Rails and it's felt like a great tailwind. We've been running a profitable BI app for around 15 years, but it's really hard to maintain. The dev team decided we want to rewrite it from .NET, Python, React into vanilla full-stack Rails. We were planning to rewrite it anyway to fix many old assumptions that turned out to be wrong and made maintenance a lot harder. It should also reduce the required coordination of backend API + frontend being that it's more cohesively developed together. But ultimately, we've enjoyed using an opinionated framework that has all the typical "web app" things batteries included and well-established. It helps discovery.
I think it works well for SaaS type offerings where you have a low number of high-value clients. We don't do high-traffic public sites. Perhaps my opinion would be different then.
A lot of people seem to overcomplicate it by bringing their thoughts on how other modern js apps are written. It would be helpful to have a specific example of some of the things you need to work through. Reading through the [handbook](https://hotwired.dev/) should get you most of the way there.
One of my colleagues recently switched from mostly react to default full stack Rails. He was really struggling with how to filter things in a table. When I showed him how I would do it, his comment was along the lines of: “I can’t believe how simple this is. Modern JS just doesn’t work this simple any more. This is like how jquery used to work but way more organized”.
I can’t say I know enough about modern js development to validate that comment. He noted something about expectations of how the dom managing things…
So, if you’re coming with a certain mindset of how to do things, try to leave that behind a moment and read through the handbook. Especially if it involves making fetch requests to do things. That’s like, the number one “you probably shouldn’t do it like that” in Hotwire.
Some of the most enlightening books I’ve read when I was first learning Ruby were Text Processing in Ruby, and Building Awesome Command Line Apps in Ruby 2. They each reveal certain features and perspectives that work towards this end, such as text parsing moves, Ruby flags to help you build shell 1-liners you can pipe against, and features with stdio beyond just printing to stdout.
Then add in something like Pry or Irb, where you are able to build castles in your sandbox.
Most of my data exploration happens in Pry.
A final book I’ll toss out is Data Science at the Command Line, in particular the first 40 or so pages. They highlight the amount of tooling that exists that’s just python shell scripts posing as bins. (Ruby of course has every bit of the same potential.) I had always been aware of this, but I found the way it was presented to be very inspirational, and largely transformed how I work with data.
A good practical example I use regularly is: I have a project set up that keeps connection strings for ten or so SQL Server DBs that I regularly interact with. I have constants defined to expedite connections. The [Sequel library](https://sequel.jeremyevans.net/) is absolutely delightful to use. I have a `bin/console` file that sets up a pry session hooking up the default environment and tools I like to work with. Now it’s very easy to find tables with certain names, schemas, containing certain data, certain sprocs, mass update definitions across our entire system.
```
# Something failed, and not everything loaded as you expected
# explore explore explore…
Just like with work in the shell, you have a really easy time iteratively exploring the problem and building up your answer. The ability to serialize your data you’ve found, and keep your favorite tools in your pocket feels extremely productive. And of course, all of this can be written in ruby 1-liner shell scripts, or more complex shell scripts to pipe in and out of other tools if desired.
It is pretty cool. I’ve been using it for 3-4 years as a queue between Ruby and Node for scraping tasks. Ruby queues the work, a node worker does the downloading, and a Ruby process parses and loads data. It works incredibly well in my use case and has saved me from having to shift everything to one language or the other. It’s been very reliable.
I think that's a great argument to consider when framing this concept to someone the first time. Avoid the `implicit return` language altogether. Instead focus on the expressions. `Implicit` triggers mental responses like "I prefer explicit over implicit" but you're right, that's just a side effect of what's really going on here.
I usually drink the DHH koolaide when it comes to Rails decisions, but I'm surprised by this decision exactly because of his arguments against being forced to write specific styles. I enjoyed default RuboCop when I was first learning Ruby to gain idiomatic knowledge, and strongly disliked after I took off my training wheels. I don't even like using `standard-rb`, which is a much more tolerant configuration of Rubocop. There are often reasons to disregard certain style rules, and I don't want to ask for permission in `# magic disable comments`.
I strongly agree with all of his descriptions of formatting and linting. I also strongly agree with him putting his personal style into Rubocop for the Rails codebase itself, and for project maintainers to be in control of what, if any, style they wish to enforce. But to omakase his style-guide into the consuming projects is surprising and against the very arguments against conforming to style-guides he presents in the first half of the post.
I'm fluent in Ruby, and have nearly zero experience in the other languages featured in this article. The `implicit return` is often something people find rather willy-nilly when first learning Ruby. As such, I always thought that was a feature explicit to Ruby and its peers (historical influences like Lisp, Smalltalk, and futures like Elixir and Crystal).
I am surprised and delighted to learn that in fact, many modern languages have been doing this. Particularly seeing this in Rust raised my eyebrows, given how system-focused it is. I'm curious from the Rustaceans out there: Is it common to use the conversational nature of expressions and implicit returns? Or does it frequently create unwelcome dissonance?
Whenever I switch to Python, JS, C#, the explicit returns catch me for a day or two. I always think "We're already reserving this value in memory to evaluate that last line, why don't you let me do something with it if I want?". But I know folks who swing the other direction and feel quite the opposite -- things feel out of place and not-obvious.
Either way, thanks for the well-written article. It has added influence to my "I think I want to look at Rust -- it feels familiar."
I can appreciate that. There's a few revelations I've had over the years of learning Ruby that highlighted "why things are as they are". Decisions that feel arbitrary and against the grain, but actually make a lot of sense when you understand the design as a whole. And I don't mean to imply "You just need to understand the roots, man!". It's probably the reason people who like Ruby are zealous about it, and many aren't. It's all about that Ruby brain glove.
Two great examples that everyone comes into right away are "Why are parenthesis optional? Why do you not have to `return`? Why do you allow such chaos to run abound willy nilly?!".
It isn't because "We just like being different!" or "We are allergic to parenthesis!". It turns out that one of the fundamental designs of the language makes it such that they don't really _mean_ anything.
I think being able to understand some of that context is really valuable to "ruby making sense" vs not. You often see people proclaim "In Ruby, everything is an object!" And it doesn't make much sense why that's a big deal. But the ramifications are actually amazing: Everything (basically) is an object -- even stuff that feels deep in the guts of the core of the language (defining a class for example). It turns out, this creates an amazing amount of _consistency_ where every single thing in Ruby from my app code, down to the low level parts of the language follow the same rules. This then makes it easy to anticipate how something works, or find out more about it.
I don't expect to single handedly fit your Ruby gloves to your brain, but here are some notes that define Ruby that I find enlightening:
- (Pretty much) Everything is an object. A `thing` is an object. A literal `1` is an object. A `class Mom; ... ; end` is an object. They all follow the same rules. If you want to learn what you can do with an object, you just need to learn what kind of object it is, and then you can find out what it knows. There aren't top-level functions to act on your objects. Ruby is all about "passing messages to objects". Your object knows how to handle itself.
- Things that look like operators are just methods on those objects. `thing > other` is calling the `.>(other)` method on `thing`. Accessing data from an object always happens via a method. There's no such thing as "accessing a property vs calling a method". It's all calling a method, always. This adds to the consistency.
- (Pretty much) Everything is an expression. A value is already created and stored in memory somewhere; might as well make it available to the next fellow. This enhances the ability to ... _express_ (sorry) things. You can expect to be able to chain things together, or leverage values as they are created.
- The whole motif surrounds having a conversation with your objects. It's less about making statements to your computer, and more about having a conversation with the context you are working in.
Honestly, these are all common bullet points that people bring up, but to me they didn't make much sense until I really held a strong grasp of the language as a whole. At some point, those bullet points became my "woah" moment of _why_ Ruby is how it is. This understanding in how Ruby was different from other languages I've used helped me understand why Ruby fits my brain like a glove. This style of creating makes a lot of sense to me.
I think it works well for SaaS type offerings where you have a low number of high-value clients. We don't do high-traffic public sites. Perhaps my opinion would be different then.