Here's an article version of the upcoming Turbo 8 stuff I presented in Rails World last week.
My favorite feature is that we are not adding new programming paths but giving you fewer reasons to abandon the happy one based on full-page responses.
A common critique of Rails is that it encourages a poor separation of concerns. That when things get serious, you need an alternative that brings the missing pieces. We disagree.
Vanilla Rails is plenty:
You are an individual contributor at heart. You like writing code and solving technical problems. You dislike meetings and ceremony. Here’s what you can do to maximize what you like and minimize what you don’t: radiate information.
"I believe Active Record is so good that it eliminates the traditional reasons for a strict separation of persistence and domain logic. I consider that a feature to embrace proudly, not a choice to justify."
Active Record, nice and blended:
Paying attention to the small stuff in pull requests is part of our development culture. This is how new people learn the in-house coding style, how we keep a high-quality bar in our code, and how we favor consistency.
Software development is so complex that it benefits from nuance. The advice of the kind "never-ever do this, always do that" should put you on alert.
"Globals, callbacks and other sacrileges"
"As long as it doesn’t smell like dogma or is used as a weapon to attack others’ professionalism, I have no problems with TDD. I just see it as a tool I don’t use."
As part of our exploratory work for smoother page updates in Turbo, we investigated using server-side diffing. Here's the story, including an internal video and prototype source code.
> Do people lose interest in programming as they age? Is it accurate to expect that older programmers are slower, make more mistakes, and would rather be doing something else such as managing programmers?
No, no and no.
I think a problem with Rails concerns is that they don't come with a user manual, and misusing them can cause more harm than good. I do believe they are an amazing resource, so I wrote some notes on how to leverage the good parts they have to offer:
"I suspect that my exposure to fancy Java IDEs early in my career made my mental model for editing code incompatible with plain editors."
Here are some thoughts on why I've been using
@rubymine
for the last ten years to code Rails apps.
Fractal is about similar patterns recurring at progressively smaller scales. To me, good code is a fractal: you observe the same qualities repeated at different levels of abstraction.
Four years ago, I wrote about considering Rails instead of the SPA path. Since then, Rails got much better with Hotwire and the improved JS bundling; I'm genuinely curious about the JS scene. It looks as messy as ever whenever I try to catch up.
@excid3
@_swanson
Godo points by
@_swanson
. To take a positive outlook, I think it encourages you to write better stimulus controllers. Stimulus is designed to work dynamically with elements that get added or removed from the DOM. If your controller modifies the DOM, it should be prepared to work…
@_swanson
We consider jobs like controllers: consumers of business logic exposed via high-level/natural APIs in domain models. Almost all our jobs just invoke a method in a model. We use a convention in many models with "<method>_later" enqueuing a job that invokes "<method>". Example:
@julian_rubisch
Hey Julian, sorry you feel that way. I have nothing but respect about your work folks, but CableReady uses a completely different approach and philosophy to what we are presenting here, even if the goal is the same. No mention to it because it had no influence on us here, really.
@ciaran_lee
Thank you so much
@ciaran_lee
! That was the development preview, which wasn't meant to be published 😂. It was planned for next week, but thanks to somebody who found it, we just published our new 37signals dev blog for good ✌️:
"I’m very wary of making predictions in tech, but you don’t have to be Nostradamus to realize that a major change will happen, and it will be fast."
Learning with ChatGPT:
In recent times I've become more intentional about using writing for thinking. Not to communicate or publish anything, just to clarify my thoughts. I've come to love the process of starting with fuzziness and seeing where writing takes me.
@rbates
We do! We have plenty of folders and subfolders. Notice that this 500 includes all the files in our /models folder, including POROs and model-specific concerns (output of "rails stats"). I shared some details on where we place concerns here
Whenever I see people telling others how to live, I can’t help wondering: isn’t figuring out what works for you already difficult enough?
Let people be.
"The failed experiment wasn't remote work but rewiring your company from the ground up. That makes for a less clickable headline, though, and is probably a harder pill to swallow for the people running the show."
Remote struggles:
If you have to drop some code, don’t console yourself with, “at least I learned something”. That’s your subconscious wanting to make software development predictive. Instead, cheers with “I finally understood the problem!”.
Erase and rewind:
@rbates
If it serves, I'm the only RubyMine user at 37signals and the extensive usage of concerns was already there when I arrived. So not an enabling factor in our particular case, but I agree RubyMine helps browsing code a lot 😅.
@rbates
Isn't it as difficult as locating parent methods? I wrote a piece on concerns here . With RubyMine, I just browse to the method definition, but the "model folder" convention should help with plain search too.
@fxn
With the M1 I remember some initial friction with Docker but that was fixed a while ago. I think the switch should be pretty smooth today. Also, the mind-blowing performance gain compared to Intel should make for unexpected hiccups.
@pmareke
I don’t really know. I know the combo of Rails API + JS-powered client is quite common but no clue about the numbers. With the JS movement in favor of server-side rendering, and with how powerful Hotwire is, I would imagine vanilla Rails is gaining popularity again.
"Any task manager can list the things you got done; the value resides precisely in what's missing in such report [...] The human ingredient is the secret sauce here."
What did you work on today?
@hopsoft
Yes! I've use it recently a couple of times to serialize POROs in ActiveJob arguments. It's more concise than the built-in custom serialization mechanism IMO.
@mhenrixon
@joeldrapper
@37signals
Lots of thoughts about services indeed. Shared the gist of it here .
The heuristic you mention of using a service whenever 2+ objects collaborate feels detached from what a service is: a domain operation that doesn't belong to any domain entity. And a…
The primary driver for people participating in public shaming is not justice but primarily hedonic – it makes participants feel good.
I revisited my thoughts after reading an article referred by
@JonathanShedler
:
@naofumi
I should write about this to clarify my own thoughts about it 😅. I think adding mandatory application-level elements to connect controllers with models makes designing good domain models harder for everyone, but especially for less experienced folks, because boundaries are fuzzy
@fxn
@asterite
Thanks
@fxn
and
@asterite
! Funny thing is that I used this last year for a case in HEY and then forgot about it 😂. I added a memorizable concern, and I was using it like that, but eventually dropped the idea as I wasn’t super convinced about how it looked with that “memoize def”
@hopsoft
@fractaledmind
@julian_rubisch
Philosophically, SR departs from how regular apps work requiring a persisting Websocket connection, while what we present is a progressive enhancement over regular web programming, in the spirit of Turbo. No criticizing at all, just saying the approach is completely different.
@jbbonin
I can't think of a case where we combine both, but I don't see a reason to not do that if needed. I can think of an example where we use inheritance on the delegated type side without STI (self.abstract_class = true), but I think STI would work too.
@_beauraF
By fuzzy I mean that they don’t always look like an operation you perform or execute, and that’s a good thing IMO. They exist to encapsulate an operation, but you can still try to find better terms than “perform” or “execute” if possible.
@pmareke
@JasonSwett
Rails’ terminology for tests is a bit confusing. We mostly use plain unit tests for Active Record models and regular models, integration tests for controllers and system tests for browser-testing the app. And we use the defaults: minitest and fixtures versus rspec and factory_bot
@rbates
Concerns are a tool and you can definitely create a mess with them, but I also believe they can enhance readability when used right. I also believe the common comparison with object composition is a false dichotomy: different tools for different purposes that play great together.
@_beauraF
Signing up is a good example (from my vanilla rails article). I can think of two I created for HEY, to change email addresses and to validate SMTP accesses. The line is fuzzy, though, I guess that’s why we don’t given them special consideration. Check how the smtp one looks:
@noelrap
@rubymine
Same here. Constants hangs whenever I start the IDE, change branches or even while just working on things. I've noticed a tremendous regression in terms of performance with the last release.
@_zzJZzz_
Yes! I don’t remember an interaction where it was totally useless. It usually nails what I ask for, but even when not, it’s still very helpful. It’s the opposite of what I’ve always experienced with other language-powered AI systems.
@dakull
@rockatanescu
@rubymine
Several people have pointed out similar reasons, so there must be something there for sure. I had never thought of that angle. Thanks!
@lazaronixon
Thanks Lázaro! Yes, I'd love to write a piece on vanilla Rails with special attention to the controller layer. When I landed at 37signals I was surprised about how vanilla they were in general.
@fxn
I’d love to see that talk. I agree that deadlines to guesstimate times and costs are useless and stress-inducing, but find them super-valuable to help making scope-related decisions. Wrote about this here
@_swanson
I see your point about ActiveJob with perform_now being like a service, but in our case we avoid putting logic in our jobs in general. We see jobs as a mere mechanism to invoke domain model operations asynchronously, so that's what our approach reflects.
@smlpth
We are not fans. We don’t use sorbet or typescript in our apps. I wrote some opinions on this back in the day . I’m sure there’s a case for them though, either for personal preferences or for organizational size.
@ciaran_lee
Thank you so much
@ciaran_lee
! That was the development preview, which wasn't meant to be published 😂. It was planned for next week, but thanks to somebody who found it, we just published our new 37signals dev blog for good ✌️:
@jonynovaretti
To use an example, I think it’s great to have a method `Invoice
#total
` and it encapsulates dealing with the “invoice line items”, instead of having a service that orchestrates that because there are two models involved.
@rockatanescu
@dhh
I think there was a traditional resistance to linters in the company, but they embraced them at some point during HEY development to enforce consistency. We use Rubocop for Ruby and stylelint for CSS, and share the linting rules across apps via an internal gem called house-style.
@adrienpoly
Not in our latest app (HEY), but we have used them in older ones, for example underscore’s templating support in some specific parts of BC3. We aim to avoid them in the new app.
"Sensations are never part of the distilled list of features we love when comparing products, and yet, they are the essential factor that makes something stand out."
@jonynovaretti
If that means that you lose having objects that rely on other objects internally when responding to messages, I don’t love it. You would lose two core OO features (composition and encapsulation). I think the key is: “not belonging” to any of the models. Then you have a service.
@smlpth
It’s 18 of PTO + 11 local holidays. Also, you have four months of 4-day weeks in the summer, and you can take a 1-month sabbatical every three years. I think it’s pretty sweet, even for European standards.
@weizheheng
We typically create tests symmetrically to the concern. So for a "Topic:Deletable" concern we would create a test "Topic::DeletableTest" in "test/models/topic/deletable_test.rb". The test exercises the concern behavior using fixture topic models.
@jaimeiniesta
@JaimeIniestaDev
@bandzoogle
! They recently announced the milestone of $100M in commission-free sales. Ran by an amazing crew of people who care. I can’t recommend them enough!
@pmareke
@JasonSwett
We should definitely improve the guides to include some meaty examples for each kind of test. I’ll see if I can help improving that.
@fxn
@asterite
It would be great if you could place the line above the “def” declaration. It’s minor, but it was something that bothered me. Also, it would be nice to be able to parametrize it, (e.g: lru_cache(max_size: 30)). But in any case, my initial affirmation was indeed wrong, ty 🙏