But those frameworks force you to do things the way they want you to. As soon as you need to deviate a little bit, and very often you'll have to, you'll have to rewrite the entire thing.
No, you don't, because then you'll be able to lean on Rails' biggest strength, Ruby. You don't know how many times I've been able to just dig into a gem's or even Rails itself, find out what's going on, and simply write what I need leveraging all the existing code.
On the other hand, when we were replatforming our Rails project into Node for no good reason, we wound up having to rewrite a ton of stuff because, well, Ruby is a great server language, and Javascript is only at best a mediocre scripting language, and ES6 is only a marginal improvement.
I think that depends on the framework and what deviation you want.
I agree that if you use Rails and constantly override its conventions, you will have a bad day. But following conventions is usually a strength, not a weakness. Most websites are not shocking cutting-edge things that have never done before, but relatively boring simple tasks that involve grabbing data from databases and showing them to users via templates. Every decision you have to make is a cost, and if you have a small team, having basic naming convention decisions and directory location decisions already made accelerates development. There's a big advantage to following conventions when you want others to work with you, because different Rails applications look quite similar in many ways.
I have not had trouble overriding Rails when I needed to override something specific.
Btw, I like the original article. There's nothing that's good for all cases, so it's very important to understand what something is good and not good at.
Sometimes, Rails’ conventions are just wrong, though.
Databases can have various constraints (uniqueness, etc.) enforced at the DB level. ActiveRecord also allows for uniqueness checks, but these are separate mechanisms. If you want to validate for uniqueness, two processes each running the same Rails app might concurrently check for uniqueness, assume validity, and then independently insert two non-unique records. At this point, your DB will throw an error. (Edit: Rails actually wraps this exception now, but if you follow the idiomatic pattern and don’t bother catching it, it just 500’s the request.) Rails is (edit: still) not wired to treat this as a uniqueness violation at all; it just has its own uniqueness validation mechanism that doesn’t even work in the most common use case for Rails.
In a concurrent environment like this with a shared DB, the only way to avoid check-and-set conditions is to just try the insert and only complain about uniqueness violations when the insert fails the DB-level uniqueness check. (Edit: Rails provides a wrapped exception for this use case, but that’s certainly not the conventional Rails way of handling uniqueness validations.)
Conventions are fine when those conventions work and actually enforce good practices. ActiveRecord in particular falls short of this. And without ActiveRecord, the rest of Rails doesn’t do much to distinguish itself from alternatives. It’s fine, but it’s not necessarily anything special.
> Rails does not, or did not when I encountered this issue, recognize the error as anything other than “whoa dude, MySQL/Postgres/whatever is complaining, check this out”
ActiveRecord has thrown ActiveRecord::RecordNotUnique for this and done the right thing for over 11 years (the exception definition got moved to a different file at that point so idk exactly how old it is.)
> ActiveRecord in particular falls short of this
> without ActiveRecord, the rest of Rails doesn’t do much to distinguish itself from alternatives.
Callbacks are pretty bad but it sounds like Rails went through several near-complete rewrites worth of changes since the last time you used it. There's a lot more in Rails 6 compared to Rails 2.
> ActiveRecord has thrown ActiveRecord::RecordNotUnique for this and done the right thing for over 11 years
I had these issues in Rails 3 when that was the newest version. I found the git blame you’re referring to here and it may have been written 11 years ago. Odd. It looks like maybe Rails 4 finally fixed this?
More to the point, the broken uniqueness validation is still there, as is the idiomatic “validate then write to the DB” race condition that leads to this issue in the first place. There’s zero correlation between stating in your ActiveRecord model that you would like uniqueness (or any other constraint) and actually enforcing that constraint in the database, where it actually works. Having an actual wrapped exception to catch is an improvement but it doesn’t fix the problem.
"This [uniqueness] helper validates that the attribute's value is unique right before the object gets saved. It does not create a uniqueness constraint in the database, so it may happen that two different database connections create two records with the same value for a column that you intend to be unique. To avoid that, you must create a unique index on that column in your database."
It's pretty clear from that text that if you want a unique index in a database, it has to be a uniqueness constraint in database itself. But that's true for any such system, that's not specific to Rails.
In most applications there are simple isolated validations you can verify without consulting the database, and other validations that can only be validated by consulting the database (such as uniqueness). Rails can do that, as can many other frameworks. I don't think that's broken, that's how things are.
Rails not only has to consult the database to validate uniqueness, but it still doesn’t know for sure even then whether the row will remain valid. So why not just insert the row and see if you get away with it?
Simply removing the uniqueness validation and relying exclusively on database indices would be strictly better. ActiveRecord dynamically infers from the DB schema which columns your tables have; it could also infer foreign key relationships and uniqueness guarantees and use them to generate pre-insertion “best guesses” about validity if the user wants to run those without necessarily inserting. But Rails is supposed to be an opinionated framework, and “just try to insert and we’ll let you know how that worked out for you, since we can’t make any guarantees otherwise” is a perfectly valid opinion.
This is not my experience. When you have to deviate, which has been quite rare for me, it’s all just ruby at the end of the day so you can do whatever the heck you want.
Every time I’ve seen an engineering organization run with rewriting the whole thing, they/we figured out 6 months later a more effective data model. That’s usually the root problem.
Rails can do many things but no framework can design a product’s data model, and when you have an excellent one Rails will sing.
Also sometimes you need some separate services for GPU/security/reasons. That’s fine.
That's why they are called frameworks, they solve standard problems in a specific way and the good frameworks provide extension points so you can customize when you need to deviate a little bit.
Can you give a specific example of when this poses a problem?
No, you don't, because then you'll be able to lean on Rails' biggest strength, Ruby. You don't know how many times I've been able to just dig into a gem's or even Rails itself, find out what's going on, and simply write what I need leveraging all the existing code.
On the other hand, when we were replatforming our Rails project into Node for no good reason, we wound up having to rewrite a ton of stuff because, well, Ruby is a great server language, and Javascript is only at best a mediocre scripting language, and ES6 is only a marginal improvement.