Live data from Hacker News

Friendly attributes pattern in Ruby

brunosutic.com

21–30 of 78 posts

Re: Friendly attributes pattern in Ruby

#21
post #19

Earlier quoted context omitted.

Hi, I'm the author of the article and the software library. I confirm I actually do use the examples from the article in my code. Here's the example that runs in hundreds of integration tests: expect(billing_pricing_plans).to eq billing_plans( 1.month => [:free, :premium, :pro, :enterprise], 1.year => [:free, :premium, :pro, :enterprise] ) It asserts what plans the customers see on the pricing page.

There's a massive gap between that pattern and the real-world complexity of billing. It's too much to cover in a comment, but this link explains the actual nightmare - https://www.getlago.com/blog/why-billing-systems-are-a-night...

We can't really tell that without knowing where the code is used, no? It's not hard to imagine a test that checks the following:

   bill = FactoryBot.create(:bill, products: [])
   expect(bill.currency).to eq("USD")
It doesn't cover all possibilities of all currencies, but it doesn't need to. It covers the one case it needs to test.

Re: Friendly attributes pattern in Ruby

#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

Re: Friendly attributes pattern in Ruby

#23
Feels odd that two feature-equivalent plans are segregated with neighboring duplicates into monthly and yearly branches. I would consider monthly Enterprise & yearly Enterprise the same plan, with modified cost & billing frequency.

Re: Friendly attributes pattern in Ruby

#24
post #19

Earlier quoted context omitted.

Hi, I'm the author of the article and the software library. I confirm I actually do use the examples from the article in my code. Here's the example that runs in hundreds of integration tests: expect(billing_pricing_plans).to eq billing_plans( 1.month => [:free, :premium, :pro, :enterprise], 1.year => [:free, :premium, :pro, :enterprise] ) It asserts what plans the customers see on the pricing page.

There's a massive gap between that pattern and the real-world complexity of billing. It's too much to cover in a comment, but this link explains the actual nightmare - https://www.getlago.com/blog/why-billing-systems-are-a-night...

That link describes billing problems of a neobank... I mean, yes, there's a big gap between my test helpers and financial institution's problems - to the point it's not related at all.

But, in principle I agree billing, even the simple SaaS stuff, is much harder than most people expect it to be in 2025. My product (linked in the original article) is based completely on Stripe Billing - and it is still very hard to avoid all the footguns.

For people wondering, I even have an example how wrong it can go: I "audited" a successful SaaS I know uses custom Stripe billing. I paid $30 for a starter plan, but was able to "upgrade" to $2k plain for free. Here's the full video: https://www.youtube.com/watch?v=YuXp7V4nanU

Re: Friendly attributes pattern in Ruby

#25
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…

Have you used it over the last few years? It has it been rapidly improving, mainly because Shopify put a team full time on it. It doesn’t take a lot of people to optimize a VM/interpreter it just has to be the right people.

And the question is always “fast enough for what?” Different languages are more suitable for different types of projects. I wouldn’t code a rendering engine in Ruby but for web apps it’s amazing.

Re: Friendly attributes pattern in Ruby

#26

Feels odd that two feature-equivalent plans are segregated with neighboring duplicates into monthly and yearly branches. I would consider monthly Enterprise & yearly Enterprise the same plan, with modified cost & billing frequency.

> I would consider monthly Enterprise & yearly Enterprise the same plan, with modified cost & billing frequency.

How would you then call the objects that store costs and billing frequency? :)

Here's what Stripe uses:

- Product: "describes the goods or services". This is where you define a (plan) name and features.

- Price: defines the amount, currency, and (optional) billing interval. Since interval is optional, Prices can be used to define both recurring, and one-off purchases.

Technically, using Prices for recurring, and one-off payments is a brilliant idea. The problem is, no one refers to recurring payments as "prices". Everyone calls a "$50 per year" option a "plan".

Re: Friendly attributes pattern in Ruby

#27

Off-topic, but unlike the example pricing plans, don’t make your SaaS’s “standard” plan $10/month. If you want a place to start, start with $50/month. Or, as Patrick McKenzie used to tell us over and over, “charge more”. (Yes, yes, I know some situations, customers, product, thinking, etc are different. But with broad brushstrokes, my advice is to not even entertain such a low price.)

This is such a broad generalization as to be useless. I use several pieces of software that are around $10/month which there’s no way in hell I would pay $50 for.

Re: Friendly attributes pattern in Ruby

#29
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 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…

Have you used it over the last few years? It has it been rapidly improving, mainly because Shopify put a team full time on it. It doesn’t take a lot of people to optimize a VM/interpreter it just has to be the right people. And the question is always “fast enough for what?” Different languages are more suitable for different types of projects. I wouldn’t code a rendering engine in Ruby but for web apps it’s amazing.

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 CPU/mem. It also doesn’t take much for a dev to write the wrong code and make them irrelevant again. Even if you get everything right you’re leaving so much performance on the table it feels like crumbs.

For small web apps Rails is fine though. I just never worked on one. The issue is perhaps no one threw the code away when it got big.

Re: Friendly attributes pattern in Ruby

#30
post #15

When I say Ruby is inefficient it’s not just the language, it’s stuff like this. I don’t fault the author but this kind of stuff is endemic. This way of handling attributes is monumentally less efficient than just using keyword attributes, which are optimized by the runtime. Unfortunately you’ll find this is every Ruby code base: tiny readability improvements that are performing allocations and wasting cycles for no…

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…

i find this structure a bit odd. i would have gone for the following pattern:

    Billing::Plan.find_or_create_all_by_attrs!(
      standard => {1.month: 10, 1.year: 100},
      pro => {1.month: 50, 1.year: 500},
      enterprise => {1.month: 100, 1.year: 1000}
    )
Post reply on HN