Live data from Hacker News

Friendly attributes pattern in Ruby

brunosutic.com

41–50 of 78 posts

Re: Friendly attributes pattern in Ruby

#41
post #22

I don't really like the API design. Perhaps in the rails-world this makes sense, but it looks really strange to me. Billing::Plan::Factory.find_or_create_by!( name: :pro, interval: 1.month, amount: 50 ) It is not only the verbosity or use of trailing '!' in a method for no real reason, IMO, but also things such as "1.month". I understand that rails thrives as a DSL, but to me having a method such as .month on an Inte…

when *everything* is an object this kind of syntax makes absolutely sense and is quite convenient

my first thought too, as a rubyist.

Yes, "everything is an object" is an essential insight to understanding ruby.

Re: Friendly attributes pattern in Ruby

#42

This is...not for me. It follows a big pattern in Ruby/Rails culture where to understand the code, you first have to understand the magic. And, it's never all that obvious where to go to try and understand the magic, because the magic itself has been imported by magic. I once was hackathoning with a colleague who was trying to get me excited about Rails, and he said, "look how great this is -- if you want the idea of…

1.method(:day).source_location shrug Look, I'm not a big fan of all of Rails' monkeypatching. That's why I don't use Rails anymore, I use other Ruby frameworks like Bridgetown, Roda, and Hanami. But there's definitely a way to dive into the "magic" and find out what's going on.

Ah, that's good to know! Yeah, he was wrong about a lot of stuff, so that adds up. He ended up in jail for a stint.

Re: Friendly attributes pattern in Ruby

#43
post #29

Earlier quoted context omitted.

Yes, every web app I’ve worked on the past ~18 years has been with Rails. I’ve seen it all except an efficient app. Sure, Ruby and Rails never bankrupted these companies but they’d all have been better off with something else. Certain cloud bills would’ve been much smaller for sure. Those optimizations to the VM are just very workload specific and become less relevant today when you’re using containers and fractional…

Can you explain why you say they would be better off? What else would be a better choice and why?

Not GP but I can answer:

Rails apps can get very expensive server wise because the “IO is slow anyways” attitude means more servers will be needed to serve the same amount of requests. For a specific bad case I worked at, the cloud bill was the same cost of 15 senior developers. And it was an app without external users (I was actually responsible for the external parts of it, it was isolated and not in Rails).

Excessive abstraction at the ORM can also make it extremely difficult to optimize db queries, so each user request can trigger way more DB queries than necessary, and this will require more db power. I have seen this happening over and over due to abstraction layers such as Trailblazer, but anything that is too layered clean-code style will cause issues and requires constant observation. And refactoring is made difficult due to “magic”. Even LLMs might find it too much.

Another problem with the slowness is that it slows down local development too. The biggest test suite I ever saw took 2 hours to run in a 60-machine cluster, so 120 hours of CI. Impossible to run locally, so major refactoring was borderline impossible without a huge feedback cycle.

The solution for the slow development ends up being hiring more developers, of course, with each one responsible for a smaller part of the app. In other companies these kind of features I saw would be written by people over days, not by team over months.

The terseness of both Ruby and Rails is also IMO countered by the culture of turning 10-line methods into bigger classes and using methods and instance variables instead of local variables. So it also hurts both readability (because now you have 5x more lines than needed) but also hurts optimization and stresses the garbage collection. If you know this, you know. I have seen this in code from North+Latin American, European and Japanese companies, so it’s not isolated cases. If you don’t know I can provide examples.

I have seen this happening with other tech too, of course, but with Rails it happens much much faster IME.

It is also 100% preventable, of course, however a lot of advice on how to prevent these problems will clash with Ruby/Rails traditions and culture.

These are just examples out of personal experience, but definitely not isolated cases IMO.

Re: Friendly attributes pattern in Ruby

#44

Earlier quoted context omitted.

1.method(:day).source_location shrug Look, I'm not a big fan of all of Rails' monkeypatching. That's why I don't use Rails anymore, I use other Ruby frameworks like Bridgetown, Roda, and Hanami. But there's definitely a way to dive into the "magic" and find out what's going on.

Ah, that's good to know! Yeah, he was wrong about a lot of stuff, so that adds up. He ended up in jail for a stint.

whoa

Re: Friendly attributes pattern in Ruby

#46

Earlier quoted context omitted.

1.method(:day).source_location shrug Look, I'm not a big fan of all of Rails' monkeypatching. That's why I don't use Rails anymore, I use other Ruby frameworks like Bridgetown, Roda, and Hanami. But there's definitely a way to dive into the "magic" and find out what's going on.

Ah, that's good to know! Yeah, he was wrong about a lot of stuff, so that adds up. He ended up in jail for a stint.

Woah, this thread escalated quickly.

Re: Friendly attributes pattern in Ruby

#47
post #18

Earlier quoted context omitted.

The usual response to this complaint in the Ruby/Rails community is that optimizing for nanoseconds, or even milliseconds doesn't matter when the same operation also involves multiple database queries or API calls. Let's take this example from the article: Billing::Plan.find_or_create_all_by_attrs!( 1.month => {standard: 10, pro: 50, enterprise: 100}, 1.year => {standard: 100, pro: 500, enterprise: 1000} ) This ensur…

> The usual response to this complaint in the Ruby/Rails community is that optimizing for nanoseconds, or even milliseconds doesn't matter when the same operation also involves multiple database queries or API calls The problem with that logic is that it’s pervasive: people have that same attitude everywhere even if no IO is being done. That’s how we get multi gigabyte processes. The whole language (and Rails) also p…

> For instance you’re probably iterating over those six plans and inserting them individually in the DB. Another approach would’ve been to accumulate all of them in memory then build and perform a single query. That’s not something people really consider because it’s “micro” optimization and makes the code look worse.

This same pitfall exists in every language. This has nothing to do with Ruby.

Re: Friendly attributes pattern in Ruby

#48
I'll add another cautionary word in with everyone else who is panning this implementation.

This is just using operator overloading to determine keywords, but it locks you out of ever using the same type twice in your signature. Notice that :usd turns into a name. What?

This is cute, but has no place in a professional software interface.

Re: Friendly attributes pattern in Ruby

#49
post #31

plans = { 1.month => {standard: 10, pro: 50, enterprise: 100}, 1.year => {standard: 100, pro: 500, enterprise: 1000} } plans.each do |interval, details| details.each do |name, amount| Billing::Plan::Factory.find_or_create_by!(name: , interval:, amount:) end end

I think this is the pattern I would reach for as well, separating the data from the execution. Being declarative about the plans (either with a config file, db backend, or simply a PORO) allows the plans themselves to be agnostic to how they are used and leaves you room to write a clean API for their creation without mixing in their definition.

Also ActiveSupport has Object#with_options which has a similar intent, but I rarely ever see it used in codebases.

Re: Friendly attributes pattern in Ruby

#50

I don't really like the API design. Perhaps in the rails-world this makes sense, but it looks really strange to me. Billing::Plan::Factory.find_or_create_by!( name: :pro, interval: 1.month, amount: 50 ) It is not only the verbosity or use of trailing '!' in a method for no real reason, IMO, but also things such as "1.month". I understand that rails thrives as a DSL, but to me having a method such as .month on an Inte…

This is a typical API design in Ruby, but the post is about a somewhat novel API design than what you're pointing out. To address some of your points:

The exclamation mark is a convention. It is used whenever a method could possibly result in an exception being raised. Sometimes it's instead used for non-idempotent methods.

"3.days", etc are Rails things. A lot of non-Rubyists don't like it but once you use it for long enough you tend to really grow to it.

As for HashWithIndifferentAccess, yes this is generally acknowledged as a mistake in Ruby's design and is rarely used in my experience. Originally, all Ruby hashes were HWIA. When they finally realized this was a design mistake they had to create HWIA for some level of backwards compatibility

Post reply on HN