Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
When Should You Not Use Rails? (codefol.io)
142 points by luu on Aug 5, 2020 | hide | past | favorite | 126 comments


Anecdotal random story bits from my current company on why you should stick to Rails:

- All of our "lightweight Sinatra(and similar) API" services eventually start to look more and more like Rails apps. Rails does many small developer convenience things well. Which you do not notice until you build this lightweight API yourself. E.g. console, logging, migrations, database connection pooling, rspec integration, i18n.

- No one likes to work with your arbitrary personal project structure conventions. Where's the code? 'lib'? 'core'? 'app'? 'api'? Also, no one wants to learn your lightweight "data mapper" pattern written from scratch because you thought ActiveRecord is too bloated and "does not scale". That being said, there's quite a few things you can arbitrarily pick in Rails projects as well which others will find surprising. But the spectrum of those choices is little narrower.

- Developers most of the time assume database connection pooling just magically happens. Sysadmins do have no desire to debug your apps. After couple weeks of back and forth you may realise that Rails does database connection pooling for you. And simply requiring 'activerecord' and establishing connection in your Sinatra app does not.

- One day someone doing production maintenance wanted to remind themselves rake task name. And ran 'bundle exec rake' forgetting to add '-T'. Default task was rspec. It dropped production database. That day many learned that Rails has safeguards against things like this, while none of those "lightweight arbitrary structured APIs" had any. Though the lesson was clearly not very good, since we did this again couple years later.


This.

There are so many things that are required in a modern web app that these "batteries included" frameworks provide for you that it just makes sense to use these for most standard projects.

I personally use Spring Boot as my default stack because just setting up a basic boot starter project gets me:

* Routing

* Server side templates

* i18n translation files

* database connection pooling

* database migrations

* ORM

* Logging

* Json serialization

* unit testing, integration testing, mocks

* CSRF

* Validation framework

* Dependency injection

* SMTP email sending

and much more, all compatible with each other

If I would start with a blank Sinatra/Express project I would have to piece together all of these myself from various libraries and my own code.


You could also start a NestJS project instead: https://nestjs.com/


yes, it looks like NestJS is a JS equivalent of a batteries included framework.


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.


> 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.


I think this is pretty clearly documented: https://guides.rubyonrails.org/active_record_validations.htm...

"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?


> It dropped production database. That day many learned that Rails has safeguards against things like this, while none of those "lightweight arbitrary structured APIs" had any.

While having a depth of defense is a good idea (and so "+1 to rspec for its default rake command not performing destructive operations" is ok), I'd suggest the bigger takeaway should've been about permissions surrounding the production database. -- You can't accidentally do something you don't have permission to do, or can't do easily.


This is a great point, so how do you handle this?

Rails doesn't have any separate facility for migration account vs production account, by default. Which because our deployment paradigm also doesn't have any such separation, resulted in an awkward conversation with our DBAs where we explained that our production login requires the DDL permissions they thought should be reserved for an admin user.

I think they're right, but there's only one place for setting the value of DATABASE_USER in each environment. So do you handle migrations by hand, out of band with a different user than production uses? (How should a regular Heroku user handle this issue, I guess is where I'm looking for a straightforward answer to this common issue that I think you're absolutely right about...)


The answer is proper 12 factor separation of concerns. The last Rails project I had a role in, I complained mightily about the lack of proper config separation and not keeping your effing config in the codebase. They fell on deaf ears, devs would edit the .env file, it would invariably make it to the repo, and database commands would invariably get run on the wrong database, including prod in some instances.

Worse, the CI/CD pipeline would break when I tried pulling the .env file out of the codebase. It took me a while to figure out why and finally keep all the config in the environment and not the codebase, I breathed a great sigh of relief when I was finally able to add .env to the gitignore.

Set DATABASE_USER and everything else to ENV['DATABASE_USER']. Better yet use connection strings instead, they're easier to manage. Devs need to hand-manage their own environment, I prefer direnv, but you can use dotenv if you have to, just dictate that .env has to be in the gitignore. Every single CI/CD platform and PaaS lets you set environment variables. So no .env files get pulled down on git pull, messing everyone's day up.


Yup, with .envs that can be overrided from the command line, the migration command gets a more privileged user set in its environment for just that command, everything else falls back to the file configured production user. Then you can't even fat finger a migration command because you have to set up the privileged environment first.


This intuitively is exactly what I thought was (should be) the answer, after spending some thought on it.

But is it documented anywhere? One of the Rails 6 bounties is for multiple database configurations to be made clear, IOW in configurations involving a partitioned database cluster, or a cluster with separate main and secondary streaming replicas for read and write locking separately; those configurations are all being made clearer in the latest Rails guides, as I understand it. There were always people that knew the right way to do it, but there was no one blessed right way to do it.

This seems like it belongs in the rails guides as a regular part of the instruction manual. Then again .env support itself is still separately provided by add-on gems, so maybe not. (Thanks for sharing!)


The problem came from fat-fingering a command for DB maintenance while running as an admin. This then ran scripts intended for a development environment. (Development environments don't need to be careful about destroying stuff).

I suggest ideally all the maintenance tasks can be done automatically, without needing the command to be run manually. Such scripts can then run in staging before in prod; or if the scripts necessarily only apply to prod, they should be reviewed carefully.

If prod admin must be done manually, then make you could make it harder to run the development scripts with such admin privileges.


But there is a release phase in Heroku, and reasonably that's the only time you should expect to need DDL permissions...

The principle of least required privilege seems to suggest that the production web server shouldn't have permission to perform DDL when it doesn't need it (so that fat-finger would have likely never happened, or never caused a problem, so long as it was only running in the context of the prod web service.)

It's just a little bit surprising that there don't seem to be any concrete solutions for this, I find it hard to believe I'm the first to whine about this issue related to commonly understood DBA best-practices. I don't know how you solve it, (maybe add a production_migrations environment?)


> Rails doesn't have any separate facility for migration account vs production account.

Writing your migrations in anything but SQL is one problem. (OK, perhaps pl/pgsql too.) Running it with a system user and database role that has access to the web app code is another.

But the major one is that web apps should use very limited database roles that only have execute permission for specific stored procedures. In any environment.

Anything else will lead to security exposures, as we continually see.

Yes, it takes longer and requires you to become proficient in SQL. So what? You might learn to make better use of your RDBMS. If you're working with data, you need to know SQL. And you especially need to know the access control features of your RDBMS.

From the perspective of data security, the answer to "when should you not use Rails?" is always.


What about removing authorization to drop a database?

Creating and destroying databases happens so sparingly, it can be done by some "root" user that is allowed to create the other users.

Granted that ot won't fix the problem if the next operation is to recreate all tables.

I'd argue the main question is: how is possible the test suite runs on the same db as the application? Even for local development it's a nightmare.


more of a Python person here, but in the python world its really similar. People feel like their service is too small / not a good fit for django and build Flask services. I like Flask, its really nice. But no 2 Flask services do look remotely similar, after 4-5 weeks the services have pulled in so many plugions that they might have just used Django or similar larger frameworks.

Sometimes people don't like to use Django because they write fairly stateless services not involving a database, its understandable but that aspect "doesn't eat much hay" tbh.

As a middle ground I often recommend https://trypyramid.com/ which has more structure, a better plugin-integration infrastructure than flask but isn't so much bound to a SQL ORM (while you still can use sqlalchemy just fine).


If you don't need a database at all I kinda understand not wanting to "sandblast a soupcracker" as the saying goes.. but I've definitely seen a very similar trend with Flask apps!

To be frank django has been my go-to for almost every app I've built or prototyped which required a db, sometimes if exposing a REST API is more of the main goal I'll use DRF and then build a minimal frontend SPA using React (served by a separate app within the project). Not sure why it's not taught/used more tbh.


> Where's the code

Honestly for anything of real-world size, that question is not well answered by rails either. Developers tend to stuff everything into either models or controllers until they learn about "service classes" and then they put all logic into service classes, with the same issues.


This is a very good comment. Every complex Rails app I've worked on, we end up having the same debates:

"Our models are too fat, let's refactor."

"Where should the code go?"

"Let's put them in a utility class."

"No, a utility class is a code smell from the point of view of true Object Oriented theory."

"Then let's put them in a service class."

"No, a service class is just a utility class with a different name."

"Then where should the code go?"

A long debate ensues, and then we end up with some crazy solution that includes everything that jeff_vader just suggested as a problematic:

" Where's the code? 'lib'? 'core'? 'app'? 'api'?"


This isn't unique to Rails apps. You'd have code organization tensions in any complex app you write.

Fortunately, for whatever you decide, you can create as many folders as you want in `app/` and Rails will detect & autoload them for you.


I think one of the things I have started to realize is how rarely people in Rails-land tend to reach for more established patterns that are generally thought to be important to writing good object oriented code.

There seems to be a real adversity among many to go ahead and create new classes as needed especially when that new class isn't obviously a model, view or a controller.

I just went over a short YouTube series DHH (creator of Rails) did where he walked through the production code behind Basecamp and I was struck by how different it looked to almost any other Rails app I recalled looking at previously.

But it seemed like there was not a whole lot that they were doing that would seem in any way out of place from a SOLID OO perspective. It's a very interesting set of videos to take a look at what a modern Rails app with a decent amount of complexity looks like as intended by the team who wrote the framework. You can find it here if you're interested https://www.youtube.com/playlist?list=PL3m89j0mV0pdNAg6x9oq6...


We use the interactor gem with great success for this.


I ran into a great "where's the code" on a "lightweight API" ruby project once. Opened up the codebase to fix an issue, and there were no .rb files at all. After opening every file in the repo, it turned out the developer of the code base had the entire service in the Rakefile.


>All of our "lightweight Sinatra(and similar) API" services eventually start to look more and more like Rails apps

Oh man, I've been working as a freelance/agency with a a team of 3 experts in nodejs for 5 years now - we've built API & products for many many clients

Our nodejs/express "lightweight" framework is looking exactly like a rails app now (migrations, controllers and models generator, some scaffolding generators)


to me, this sounds more like "Ruby tools don't have sane defaults unless you use Rails"

Some examples from crufty Java world. You magically get connection pooling with any JPA provider. If you import a logging library, it magically works. Flyway and Liquibase db migration tools have built-in failsafes, and "just work" with defaults.

I think Ruby suffers immeasurably from most things being built for Rails. In Java and Python, there's so many options that everything is forced to be modular when running standalone and play nice with various frameworks.

Ruby is loved for Rails, but Rails is slowly killing the langauge by making it hard to do anything without it. For most purposes, Ruby is Rails. To me this is a good reason to use a different language where I have flexible options


If you import log4j it automatically does exactly nothing and to a total beginner it's absolutely not understandable why a logging framework wouldn't log.

Unless they changed something in recent years, but to me it's been the worst logging framework I've ever seen.


now that everything is using SLF4J all you do is import it and put a config file in one of various supported places in project


But most Ruby devs don't want flexible, they want Rails. And this article makes a good case for why that's a good thing. It's also happening in php world where it seems 90% of projects now go with Laravel.


IMO its happening to languages that are no longer popular enough to maintain multiple frameworks. The difference with Ruby is that it was always mostly rails. I'm worried Ruby will descend into a Rails death spiral. Depending so heavily on one framework stops innovation


I think you’re right to worry, I wouldn’t be able to think of any reason to use Ruby instead of something else if Rails is off the table (in terms of ecosystem, not talking about personal preference here). Which is a shame because I like it the most of all the scripting languages, but Rails just sucks all the air out of Ruby.


Unless you're into data science, what can u do in Python that you wouldn't be able to do in Ruby? Most "famous" gems are still well maintained afaik. The Ruby eco system is still nothing to sneeze at.


I recently used Ruby, Roda, and soap4r for a JSON-to-SOAP gateway project. We didn’t need all of the ceremony of Rails (no database), and needed more velocity than Java could give us, and none of the Elixir/Erlang SOAP libraries actually worked with the WSDLs (plural) we had.

There’s a _lot_ of reasons to use Ruby if Rails is off the table.


Ruby became niche long ago, just have to accept it. It doesn't mean you won't find work in Ruby if that's what you want, plenty of companies depend on it. Rewrites in different stacks are quite impossible sometimes.


Regarding dropping the prod db. I remember when Github dropped their production database because they had prod in their test env setting.

https://github.blog/2010-11-15-today-s-outage/


yeah the claim that Rails is somehow "too much" for an API-only application smuggles in a lot of assumptions about what you might be using instead (and how good your team is at designing their own patchwork of libraries and how desirable that is).


Spot on. Very well said.


Funny, I never bothered to not use Rails because I can go from zero to everything running on Heroku in 15 mins now with SSL on a domain and all the shit. Being incredibly lazy I do this even for non-DB stubs, using the free Heroku Postgres add-on. So I don't even know if it's possible to run Rails without a DB. I did try `nulldb` last time but migrations broke, which is odd.

Honestly, even if doing JSON API -> React frontend, I'd still do it. It's too easy to get it right with Rails.


Yep, I built a salary calculation app with Rails and React, using it as an API and it still saved me tons of time, and the business logic was well suited to Ruby with some nice unit tests


> I’d still start out in Rails, personally. If you’re building something quickly to see if anybody cares, I know of no framework that comes close to its productivity.

I've used RoR for 5 years, spring boot/kotlin for 2 years and Django for a year. Once you've set up the workflow, there isn't much difference in terms of productivity. The pain point is setting up the base project and keeping it updated as library versions change.

What django and spring boot are missing are the commandline generators for entities, controllers and views and a quick setup.

Django and spring boot need a little bit of careful setup. Django needs its settings, applications array, middleware declarations, etc. Once you get over this and start adding your entities, views and routes it goes fine.

Spring boot requires a rigid folder structure, application property files, a careful setup for migrations and logging. Liquibase setup, etc. It doesn't get off the ground easily. Once you start writing your entities, repositories, controllers, services, views it goes smoothly thanks to lazy initializers.


springboot? it cannot get easier than this: https://start.spring.io/

I personally choose Java/SpringBoot, it has a steep learning curve, but has strong typing, runs as fast as most C++ code out there, plus you can do async.

and if you want something fast but that is as easy as Rails and runs on Java/Groovy/JVM, use Grails.


>springboot? it cannot get easier than this: https://start.spring.io/

That doesn't seem easy at all. Immediately I am presented with 5 choices and then a list of 50 things that I might want to add to my application. Some of those 50 choices seem to be mutually exclusive and I don't have any way of knowing which I might want or why.

Rails? Just rails new myapp.


To be clear, your choices are:

Build system, Language, Spring version, Java packaging, Java version

The rest is naming.

That is not complex to me.

As for the dependency options... well, they are dependency options. I'm sure you have no problem with adding gems to a rails project, even though there are thousands of them that may be mutually exclusive! A non-ruby dev might not know which gem they want and would have no way of knowing what they want or why!

rails new myapp is exactly the same as doing this with no dependencies.


If you want something more built out from the start you could use https://www.jhipster.tech


Great news. Django 3.1 was released today which includes support for async views. The ORM is still not async, but it's a big step.


You mean "static" typing. Ruby is strongly typed, it just isn't statically typed.


> if you want something fast but that is as easy as Rails and runs on Java/Groovy/JVM, use Grails

Is there a reason you'd use Grails over Rails on jRuby?


Haven't used grails, but back in 2012, JRuby used to generate so many in-memory classes, the class space would run out and we didn't really have a garbage collector for that. It came a few years later. Things might be better with JRuby 9000 and java 1.8+ these days. I presume Grails didn't suffer such problems.

Note: I see that torquebox was discontinued. It was a simple way of getting your rails project to work with JBoss and had many nice utilities like infinispan and stomp. Good old days.


If you are building something new with Django where Rails' generators would have been helpful, check out https://djangobuilder.io/ - it lets you quickly list out the data model and then it generates the whole code skeleton for the site.

It's not a perfect replacement, but it can save time if you are starting a new project with a lot of boilerplate code.


I can't imagine developing an application this way anymore.

From the article: "Rails has less to offer an API server that speaks JSON over the wire."

Everything we do works this way now (JSON over the wire). Everything is a RESTful API first. We build a UI that uses the API, but the business function is served completely from the API. Why? Because if the function is valuable, then some other program will likely want to exercise it programmatically. All of our capabilities are built to be composed programmatically by default.

The additional effects this has on our organization is that we can often turn around new integrations in hours. They're all API calls. Other developers can kick the tires through fully secured Swagger UIs before a pretty application ties it together. And everything we build can be combined into multiple different UIs anywhere in our suite of applications.

Every API is built with the assumption that it is out on the public internet, subject to all the security and scaling problems that entails. What it has driven us to do is get very good at standing up hardened, simple services that host a consistent API-style.

We build UIs, but we assume the real value is in the functionality and the data, not in one specific form of its presentation.


Less doesn't mean none. Rails (and frameworks like it) serve multiple purposes -

One is serving server side rendered pages. One is providing non-functional requirements such as security (including browser based security, some of which is tied to the pages served, i.e., CSRF tokens). One is providing CRUD functionalities.

The first one, and some of the conveniences of the second, you'd not benefit from. The last one, and some of the core functionalities of the second, you would. So, less to offer, but not 'nothing'.


> Ruby isn’t designed for a team of 200 programmers where you don’t trust some of them.

Never mind a team of 200, even on a team of one person I don't trust myself! That's why I'm using typed FP. I don't trust myself not to make clumsy errors. I don't trust myself to be disciplined and write all the tests. I don't trust myself to not design a naïve architecture which I'll have to do a huge — and dangerous — refactoring on later.

This is why I'm not using Ruby/Rails any longer.


It's not really about trust, not even about skill.

I submit that this sentence:

> Ruby allows for a lot of non-local effects. Whether that’s monkeypatching, writing to a database or creating new types at runtime, Ruby isn’t designed for a team of 200 programmers

can and should end there, full stop.

The bigger your team, no matter how skilled, the more often someone somewhere will make a mistake and cause side effects they didn't intend. And when these side effects can manifest in unfathomable and hard-to-analyze errors in a completely different part of the codebase, you're in trouble.

Obviously, this can happen in any language, but some enable it a lot more than others, and some make analyzing weird behaviour a lot easier than others.


While I agree that types are a great tool and ensure a level of robustness in your code, I do think there's tradeoffs. Rails offers a ease of use and robustness in infrastructure that is very compelling.

I could write a web server in OCaml or Rust and it'd be wonderful code that has a lower rate of failure per line. But it may not have the same depth of logging, connection pooling, configuration, etc. I may have to write some of my own auth logic, as I had to do when dealing with JWTs in Rust. Do I trust myself to write auth logic? Even with types it's quite easy to write a logic error that leads to a vulnerability. Meanwhile I'm pretty certain Devise is rock solid.

Also things just take longer in less mature stacks. I had a wonderful time writing a web server in Rust but I'll probably never do it again unless I need serious performance and Rust's libraries mature a little. And Rust's library ecosystem is a lot better than say, OCaml's or Haskell's. Robust code is great, but it's meaningless if you can't get stuff done.


I've been getting stuff done in Haskell for several years now. The ecosystem is certainly mature enough for me.


In the age of structured web APIs, the lack of types also makes generating those API services needlessly complicated. Rails was pretty nice in the days we were dumping out simple HTML pages on the fly, but those days are gone for most projects. While it has certainly tried, Rails has not done a good job keeping up with the times. A lot of that falls on the limitations of Ruby.


I think "refactoring is much safer in strongly-typed languages" is a point against using Ruby. It's my understanding that some of what counts as "writing tests" in Ruby would be covered by static types in other languages. -- But it's also not the case that that having types excuses the developer from having tests. (Maybe with dependent types?).

This point reminds me of some essays from Steve Yegge where he argued that one way of categorising programmers was into two groups: 'conservatives' who liked languages like Haskell where you could have confidence the code worked if it compiled, etc. and 'liberals' who liked languages like Ruby, where if you notice something breaks you just fix it; just write some feature to see if it works or not. -- I'd think for prototyping it's nicer to have the latter attitude, and it's more reasonable you'll have an understanding of the system since it's so fresh. For long term maintenance, the former is surely better


The Rails/Ruby community has probably the most developed testing culture out of all the programming communities out there, because tests are so critical when you're writing in a dynamic backend language. You don't have the built-in "tests" from a compiler that check every argument and method reference. And then you don't have as fast and obvious a feedback loop as you do with frontend languages.


I think you're right, and it was more Ruby (and other similar languages) that I was abandoning rather than Rails specifically. I like Rails.

I can't speak for everyone, but my perspective comes from a place of practicality as opposed to philosophy. I (and also my team) try to push the domain we're modelling into the types, but we also write a whole bunch of tests (which includes isolated, integrated, and property-based tests). I've never understood the argument of one vs the other — it's great to have both. Programming is tough so I'll take all the help I can get.


I generally feel the same way, but I've found Rails's testing setup is convenient enough to mitigate that uncertainty with relative ease. Writing some 3-5 line tests for each feature you write (which is necessary regardless of static testing) accomplishes a lot.


> That's why I'm using typed FP.

Typed FP for Webdev is surprisingly enjoyable. Working with Frameworks/Stacks that translate FP code to HTML/JS is an absolute joy.


Commenting so hopefully you'll get notified as I too would like more information. Based on your username, I'm guessing you are referring to F#.


Do you have any favorites to recommend?


I know you weren’t asking me, but you didn’t get an answer so I’ll chime in.

At work we use Yesod, and it’s been working great for us. A more detailed explanation of what we use can be found here[0].

[0]: https://serokell.io/blog/haskell-in-industry-riskbook


“I can’t be bothered to do anything properly. It must be a problem with Rails.”


What does "properly" mean in this context?

All of my income is from my own software businesses. I used to try to do this with Ruby, and there were lots of problems. Now I do it with Haskell, and there are fewer problems. This ended up being a very good business decision for me.

What is the return on investment for me as a businessman on bothering to "do anything properly" when the resulting product is both cheaper to write and is more stable if I just use a better tool? What do I actually gain? Some kind of nerd-honour?


"I recognize that no human is fallible, so I use tools that don't assume infallibility"


Safety aside, I find going back to “guess the source of the meta magical pattern-named method” intolerable these days. Onboarding to, taking over, or pinch-hitting in a Rails codebase is miserable unless it’s tiny and perfectly maintained by developers with excellent judgement and taste—and of course this is almost never the case.


I think that's really the answer. We all mess up and using tools that make it easy to mess up is a problem... It will happen and it can happen in very bizarre ways.


The article points out a nice pivotal moment where Rails stops being as useful: when the server costs are higher than the engineering costs. I'd add that this shouldn't be a comparison based on a prediction, but on an actual monthly report. Until then, I find Rails to be a perfect tool that enables devs and companies to keep working on what matters.


I totally agree with the thrust of this. There is one other concern, though. I actually just submitted my own blog post on how infra costs can break a business. https://questinglog.com/costs-are-part-of-scalability/

If your business model brings in $X per unit of usage your hosting costs need to be < $X per unit of usage or else no amount of engineering will make your business profitable.

One of the founders of Twitch recently tweeted about how using Amazon's new service for Twitch-like video streaming simply wouldn't be feasible for building your own Twitch.


You can pretty much substitute "Django" for any case where Rails is not appropriate. That is, they go hand in hand and both have similar uses.

But there are very few cases. Fewer I'd say than this article lists. Low level programming, and yes perhaps event-based systems. [1]

I find people just like rewriting their own batteries and conventions. Fun, no doubt, and a good learning experience.

I challenge any microframework / library user to surpass the productivity of a mature framework like Rails or Django. I argue nothing comes close.

And I don't mean "for the 5 second MVP". Perhaps your app won't need some of the batteries, but your app will need logging. It will need database migrations. It will need forms and inputs. It will need media uploads. It will need to send email. And so on.

Future proofing too much is an issue in the industry. YAGNI after all. But this is the opposite case. I deem it: YAEGNI. You are eventually going to need it.

[1] Django 3.1, just released yesterday, begins treating async as a first class citizen. I was initially opposed to this, given 99% of the time you don't need async. But I suppose it's nice to have and drop down to.


It is very difficult to explain to someone who has not used Django/Rails just how productive they are, especially Node.js devs. I started off with Django, so when I moved to Express for a project, I was immediately slapped with the reality that I needed to write my own forms and admin site. These are some of the things that you simply do not have to worry about with the aforementioned batteries included approach of Django/Rails.


Exactly. Every NodeJS tutorial starts with: "look how easy it is to write a server and listen on port 3000".

Yea it's easy. But why are you writing a server? That should be the least of your concerns.


Well, I need to write a server if my requirements don’t fit a server side rendered application and/or my usecase does not fit the absolutely anemic performance of Rails, which also applies when using it only for an API (it’s not Ruby’s fault btw).

The classic response is “zomg premature optimization bad”, but some of us work on more complex applications than simple database CRUD.


Well if you need full package of migrations, html rendering etc... Rails is still a good choice. It is 2020 not 1996 we have choice now and picking up framework/solution based on our needs is standard.

Ruby/Rails and Elixir/Phoenix has one big thing, REPL. No matter what you choose. C++, Go, Rust, Node etc... getting solid easy to use REPL is effort and not built in feature.

in Rails/Phoenix you get it for free. That is a great perk!


How is REPL not a built-in feature of node ? Adding a REPL to any interpreted dynamic language should be trivial.


Rails has another degree of REPL in the console, which allows you to execute Ruby with all of the Rails libraries loaded. I can't count the number of times being able to debug on Rails console has saved me.


Well, for one, Node's REPL is horrible. The big main issue is that Javascript's architecture relying heavily on concurrency will break your workflow in all sorts of ways. Ruby's pry has been around longer than NodeJS. It's rock solid.


I have to admit I don't use node REPL a lot but what issues are you having ?

Once they added await to REPL I didn't really have issues with testing async code, but again I only did small projects with node.


It's been a solid year since I messed with Node, and I refuse to use it again, so don't have anything concrete.


Semantics of execution can vary wildly. Node is somewhat usable except for the "REPL is not async context" which leads to annoying tricks, but for example Python is very non interactive due to code reload behaviour


>Node is somewhat usable except for the "REPL is not async context" which leads to annoying tricks

What do you mean by this ? It's been a while since I was able to use await in REPL, maybe you are referring to something else ?


Was either Node v10 or v12 (I suspect the latter), as it was in december 2019 or so and I tended to use latest stable. Await in REPL definitely didn't work, maybe there's tooling to fix that? Unfortunately Node is not my main, or even third-line tool, but I had to work with Strapi and it was definitely a common issue that I couldn't use "await" on a function.


Pip install ipython and enable autoreload. Problem solved!


Tried that, didn't work.

Maybe I'm just too used to Lisps (and Ruby, which also handled it better)


I've used Rails for various products for 10+ years (and plenty of other tools for comparison) and really appreciate it for the productivity, conventions, philosophy, etc. But after spending most of this year with Elixir/Phoenix, I don't think I'll ever go back.

In my opinion, Phoenix competes well with most of the strong points of Rails, and avoids many of the downsides - including the ones discussed in this article. It's also built on a more performant and robust foundation (Erlang/OTP). You may not truly "need" some of those advanced foundation features at first, but at just the same point where a Rails project starts to struggle, Phoenix really shines.

Phoenix is not the solution for every app, but for every app where I would have used Rails in the past, it's looking like Phoenix from now on.


Personally once I started working with TypeScript, I don't want to go back to an untyped dynamic language. Its just too painful, even for personal projects, and tests don't fix the pain (at least not at the expense of not doing test-induced design damage which is also painful)


Has anyone heard of good resources that teach you "when to use X" instead of "why you should use X"?

It's somewhat understandable that the official websites of frameworks/tools/libraries/languages/services tend towards espousing the advantages of the thing and leaving out most of the drawbacks. That's just how the incentives are aligned. But even the average blog article about a tool posted on HN rarely talks about what situations it is most effective, and what situations it should potentially be avoided. I suppose it's a natural consequence of blog authors only knowing so many tools, no single person knows them all.

I'm not saying this information isn't out there – just that it seems like the exception rather than the rule.


> Has anyone heard of good resources that teach you "when to use X" instead of "why you should use X"?

One of the goals of a podcast I started last year was to answer that type of question and more.

It's mainly a podcast where I talk to someone new every week to get a better understanding of why they chose a specific tech stack to build their app but it also goes into the "how" since we talk about how they build and deploy their app, lessons learned along the way, etc..

A list of episodes can be found here: https://runninginproduction.com/podcast/

Each episode has a "Motivation for using ..." time stamp so you can jump to that part of the discussion. It's usually within the first 5 or 10 minutes.

The goal here is to have a bunch of real world examples of why and when someone used X and they usually spend a few minutes talking about why they chose that instead of something else too. There's episodes ranging from solo developers working on a side project to high traffic services handling billions of requests per month.

The idea is to have loads of practical examples instead of trying to theory craft hypothetical scenarios.


Thanks for that! Coincidentally I’ve had that podcast on my list for a while, guess it’s time to give it a listen.


No problem.

Around 4-5 months ago I implemented a custom player so now it's really easy to listen at 2x speed, and there's always been timestamped links to quickly jump around to specific topics.


Is Rails still (I have not used it since 2016) incredibly hard to migrate after a couple of years? I have java/spring, asp.net (core) and php (no framework) in production and I can upgrade after a decade not touching and everything still works. I had Rails upgrades after 1-2 years that were absolute nightmares. I'm not sure about other people here but I really don't want to suffer through that 'for fun' while I don't have to. Speed of developing is not so much faster that I would take this for granted. But maybe things changed.

Edit; it was a question, what's with the downvotes? It used to be bad; I'm not the only one saying that. It's normal (Laravel, Node etc have it as well; it's fast moving).


I don’t get why you’re being downvoted. You’re right, a while ago that was a major headache. The good news is that it’s now much better!

- Bundler makes keeping track of gems trivial

- Rails defaults are explicitly version-bound. E.g. you can update to Rails 6 and keep using Rails 5 defaults.

- There are now extensive official guides on how to update.


Good to hear, thanks! Maybe i'll try it again as it was quite nice outside updating/upgrading.


The upgrades prior to 2016 were definitely painful, especially 3.2 -> 4.0. In recent years, there's been less changing the underlying framework; new versions tend to just add new features.


Curious, what does the author mean here:

When It’s ‘Just’ an API Server: Rails has less to offer an API server that speaks JSON over the wire. A lot of its HTTP security doesn’t matter for that case (e.g. SQL injection safeguards, XSS prevention.)

It seems to me that SQL injection safeguards would still be needed in an API service. OFC XSS isn't relevant though.

(I word it like this to be charitable in case I misunderstand what the author was trying to say, but if they are trying to say what I think they are, I think they are wrong.)


I have no idea what TFA meant, but if they meant what you assume they meant (and I tend to agree with you) then the whole premise is broken. I'm not aware of "preventing SQL injection" as any particular advantage of Rails (nor even of ActiveRecord).

Obviously if you write sufficiently bad ActiveRecord method calls, you can trick it into doing SQL Injection. So it's not magic. (Indeed, I view that as a strength of AR; it has robust escape hatches. I hate ORMs, but I hate AR less than any other ORM.)

And equally obviously, every credible database driver written in at least the last 15 years has robust support for prepared statements and tolerable documentation on how to use it that can be understood in literally a few minutes. Which, of course, is exactly how AR gets it right in the first place.

(And I only have to say "in the last 15 years" because of the dumpster fire that was turn-of-the-century MySQL.)


Honestly, this article reads like blogspam.


I love Rails and it has done a lot for my career. However, after 6 years of Rails development I find myself far more likely to be reaching for ‘dotnet new’ rather than ‘rails new’.


What language/framework does that use?

I played with F# and liked it enough but didn't use much of a framework (Giraffe). Biggest hurdle was there wasn't a good up-to-date setup guide I could find for macOS.


ASP.Net Core 3.1 is perfectly usable on OS X and by the end of the year .Net 5 promises to be truly cross-platform.


Just another critical article without substance to steer up rails community and get some readers.

That API part in particular is dead wrong. Gives the feeling Rails is not good for anything.


The scaling factor for each app is really the way it interact with slowest part of the API. If it is Database than no matter how optimal your Rust code is, you might fare in the end not that much better than Rails just because you hinged everything on eg. Postgres.


Db is slow anyways is the default excuse when using slow web backends.

The one I'm currently working on is blazing fast and were truly hitting postgres limits... At nearly 50k requests a second.

The DB is rarely a bottleneck if you have sane structure and indexes. Ironically the slowest frameworks also tend to generate terrible queries (ActiveRecord, SQLAlchemy), so you end up with a slow database and web framework


I agree with you. I don't think anyone can convince me to use these bloated frameworks on any project ever again in my life.


The assumption here is that you will spend more CPU cycles in your rails app to hit the ceiling of the DB than you will with a performing rust app. So even though you will have the same upper ceiling of scale, reaching it will require less compute and therefore money in your api layer. Whether the difference is significant enough to balance developer FTE depends on the org.


You should probably listen to the podcast referenced. The hot take that this is trollish or clickbait really isn't accurate.


Something not touched on in this post but I’d like to know is the preference for using Rails with a Single Page App built in a framework like React.

When our team determined React to be the way to go for the front end, I looked at all of the parallel dependency management and the backend complexity (one might call “sophistication”) of Rails, and ultimately decided to make the backend in Node instead.

Has anyone here experienced pleasant success with RoR+SPA?


Phoenix as a Rails successor does fix most of these issues, in a right way.


You can build "API only" apps with Rails using rails-api[1]. It removes all the bloating that comes with a typical html rails app.

[1]https://guides.rubyonrails.org/api_app.html


I think this is a pretty reasonable and balanced article, actually, and I think it straight-up admits what many instructors lie about:

If you already can write a Hello World back-end in some other language/framework, and you're now learning Rails just so you can do Rails+React, that's a dumb waste of time.

(I'd say the same thing about most frameworks for most purposes, but it's especially true with Rails. But doing Rails+React when you're already fast and competent with Rails, that's a very sensible choice in most situations.)


I use Ruby to create API servers these days. For a couple of APIs, I used Rails API. Now I use a combination of dry-rb, rom-rb, roda and other pure ruby libraries to create APIs. There's a growing movement within the community to move Ruby off Rails.

Before the API thing, I also developed websites with fullstack Rails. The experience with fullstack Rails and Rails API have helped me a lot to understand the requirement of a backend framework. I'm forever endebted to Rails for this.


I agree with the article. For instance, I was using Rails a lot in the past when I was bootstrapping small shitty projects to get a "taste of the user" and validating my ideas. For more complex projects I am using Ruby business oriented frameworks (trailblazer is a great choice here!) but I flavour it up with some ActiveRecord and ActionMailer as its very convenient. In general, the whole Rails framework became too much for my projects and to blown up.


>>When It’s ‘Just’ an API Server: Rails has less to offer an API server that speaks JSON over the wire. A lot of its HTTP security doesn’t matter for that case (e.g. SQL injection safeguards, XSS prevention.)

I disagree on the SQL injection part, Api calls can contain user input which in turn can be used to perform SQL injection, I am not sure what the difference between a website and an api server is in regard to this concern.


Answer: When Elixir/Phoenix exists


That's sure the answer I arrived at.


Did Rails for at least a decade, but won’t go back after doing elixir/phoenix for a couple years now




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: