Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
A Love Letter to Ruby and Rails (jmarchello.com)
153 points by jmarchello on Oct 26, 2021 | hide | past | favorite | 65 comments


> If you're a developer or entrepreneur about to start a new endeavor, I highly recommend you consider Ruby for that project. You will find it to be a competitive advantage as you will be able to grow and adapt faster and with fewer resources.

Like a lot of people in technology, I love to play with new stuff, but as someone who's seen things come and go, it's really difficult to recommend something other than Rails for most use cases. It's got so much that you can just include in terms of libraries and it's got a good community with good values. It lets you write down a first iteration that's solid and can be incrementally improved on and taken in new directions as the business learns what's what.


I'm almost embarrassed at how fast I can get things done with Rails. It's so easy and it just flippin works.

I devote time to keeping up to date with alternatives, but the costs of switching are high, and the ROI just isn't ever there.

(I work at a university these days, where our problems are different than when I worked at a startup, but speed of development and the relative stability of the platform are always important no matter what you're doing)


I've found that Elixir / Phoenix has given me the level of productivity / flexibility that Rails used to have. Perhaps it's not quite on the same level of productivity but long term maintainability seems to be easier in Phoenix, I think from the functional programming aspect / lack of magic.

Things I love about Phoenix over Rails:

- Background / periodic jobs are baked into the same runtime as the server. You can install Oban in about 5 minutes and have it generate periodic jobs without any messing around with additional Ruby processes or custom databases (i.e. Redis, although background jobs are great on Postgres these days)

- Anything real-time is given to you out of the box. Rails has ActionCable but like the background jobs above, it needs to be setup as a separate process. Livepage makes this very straight forward.

- Phoenix sacrifices some of Rails' magic for verbosity but I think that's a better trade off long term.

Things I've loved about Rails:

- The eco system.


Worked for a company that made extensive use of elixir. It was a disaster, not due to the technical stuff, but because of every other aspect of a technology choice:

- Near impossible to hire experienced people - We had to write from scratch a lot of things that are "free" with rails/django/etc - The paradigm change takes time to learn and get it right - Some people were happy to learn it, others didn't want because they knew they wouldn't use it ever again - Many missing libraries in the ecosystem. Some libraries are poorly implemented or abandoned. - Googling for problems wasn't easy, there aren't as many answers out there as you can have for Rails, Django, etc...

But yes, the Erlang VM is awesome, can't disagree with that.


I really really want to love working in Elixir and Phoenix, but I keep stumbling at the lack of database connectivity in Ecto. Anything other than Postgres seems very unsupported. When I took another look six months ago or so, there wasn't a workable SQLite Ecto3 Adapter...

I'm guessing it's just a use case mismatch.. guessing most of the Elixir/Phoenix apps being built aren't needing to integrate with existing boring backoffice stuff.


> guessing most of the Elixir/Phoenix apps being built aren't needing to integrate with existing boring

I mean... it is, but that's usually done through an API to an existing back office app. For us, we have Elixir sitting there doing orchestration, but it doesn't take over the entire responsibility of an existing app so it doesn't really make sense to rewrite or extend any part of the API into it.


Periodic jobs are one of those things that as soon as a project is a success, and has real life users, you need it.

But as you imply, rarely built in.

They've recently built it into asp.net core, but it feels unfinished, the syntax/api sucks, it's poorly documented, and unclear how to use.

Hard to get right for such a seemingly simple concept!


For me that eco system is worth so much. I find the docs in elixir so sparse, and often land on rails docs because google can't find enough to fill a page. - SO is the same, I get rails answers. Active Jobs make background tasks so insanely easy.

I find with elixir I'm adding a lot of boiler plate.


If you start scaling and running more than one instance, each instance would run the same periodic jobs. Shouldn't periodic jobs be off loaded to a queue and worker(s) to process to the messages?


With Beam, you form a cluster. It's a stateful system, so you leave only one node to do this scheduling...


The hard part for me is the lack of official SDK's. I really do not want to have to deal with the Stripe and Auth0 HTTP API's and potentially mess up Auth and/or billing. Those are not trivial things.


I think other than Rails, it is also worth considering the other full-stack frameworks: Laravel, Django, and Phoenix.

I've spent the past year or so on and off building things in Phoenix. Elixir is an amazing programming language, and LiveView is pretty cool. If you are building something complex, I think Elixir and Phoenix solve a bunch of tough things for you out of the box, thanks to the BEAM. However, I find that the sort of websites I like to make are not complex and pretty much are content-based with a tiny bit of CRUD.

Because of this, I've lately been exploring Laravel. PHP is pretty much _the_ language for content-oriented sites, and it isn't going away anytime soon. I've been looking into CMSes like Statamic and CraftCMS and of course Wordpress. Laravel seems to have as big of a community around it as Rails. I've also noticed that many of the things I've used in the past couple years have come out of the Laravel community, in a sense, such as Tailwinds and Alpine.

IMO, most of the frameworks are somewhat interchangeable. The reason why Laravel interests me is because I no longer want to chase new and shiny things. PHP will always be around, and I always tend to like content-oriented sort of business models more. I figure that I might as well embrace the ugliness of PHP and stop thinking about programming languages and frameworks and instead spend my time thinking about what I can make.


We differ greatly in enthusiasm regarding PHP. I'd not come near to it, and I know it has gotten a lot better. The language you pick is the hardest choice to come back to later on. PHP is just not very well made.

Django is nice, Rails is much better architected and Ruby is just a nicer language than Python (cleaner to read, more functional-goodness built in, better APIs both in std libs and gems).

I agree Elixir+Phoenix is a great choice too.


Laravel appears to me to be similar to Rails on PHP. Symfony is Spring on PHP.

I'm a Symfony fan, and have never seen the need to use Laravel.


Shouldn't Spring and ASP.NET Core be in here too?


Where would you say Django finds its niche?


In dev shops where python is needed for other tasks like data science or ML


You could just offload ML related tasks to a Celery queue I think


You can, but Celery has pretty tight integration with Django so it's easy to write a Django app that includes a bunch of Celery tasks if you're going that route. Also, it's not entirely straightforward to run a web app written in another language which runs some of its async tasks via Celery. If you know you want access to the data science ecosystem of libraries Django is a solid choice.


I mean that for dev shops that specialize in python, it would make sense to create web apps using Django/Flask to enable easier hiring and transfer of skillsets between projects.


I think Python has a really nice integration story with C with cffi, cytpes, or Cython. That makes it nice to write the high level stuff in Python, and have it call into the faster language.


I have heard this many times from many different people that I respect. However, I'm just not experiencing "the joy" after several attempts. Maybe it's just not how my brain is wired.

It's probably not helping that I'm not starting from a greenfield project. Needing to go through and understand other people's code with an out of date Rails and its dependencies have made me spent most of the time on infrastructure. The entire test suite takes 50-60 minutes to complete with a lot of integration tests hitting the database.

Do people allocate a certain amount of time just to upgrade Rails? It seems like there's an expectation that Rails is 1) used to start quickly with scaffolding (great for hackathons) 2) constantly running app and cared for to not diverge from the latest version

At this point, it's one of those love-hate dysfunctional relationship with Rails for me


> Do people allocate a certain amount of time just to upgrade Rails? It seems like there's an expectation that Rails is 1) used to start quickly with scaffolding (great for hackathons) 2) constantly running app and cared for to not diverge from the latest version

I once started an interview with the question... "So, what version of Rails are you stuck on?"

The interviewer sighed loudly.


Rails is the only framework I've used much that's made me feel like regular upgrades are plausible/possible - things spit deprecation warnings, there are reasonable release notes, and most apps have decent test coverage so you can tell what breaks quickly and often fix it with little effort.


>Do people allocate a certain amount of time just to upgrade Rails?

Is there a framework or technology that doesn't need constant maintenance? I feel like Rails is actually quite low maintenance. Major version changes are rare - Rails 6 was released in 2019.


Typed languages and explicit-over-implicit frameworks are much easier to upgrade. I worked on a large project based on Wicket and we would curse the library developers for making their classes "final" so we had to copy-paste rather than subclassing, but the result was that even major version upgrades were completely routine and painless.


Java being typed hasn't helped my company move on from Java 8 :'(


The blockers tend to be the parts of "Java" that aren't actually Java and aren't typed, such as Spring or Hibernate. Blaming Java for that is correct - there's no point having a typed language if it's so inexpressive that people layer untyped hacks on top of it in order to be able to actually do anything - but blaming types is a mistake; if you use a language that is properly typed without compromising expressiveness (i.e. an ML-family language in the broad sense) then you can genuinely get that ease of upgrade because you don't have to step outside the language to get things done.


Moving from Java 8 to Java 11 or even Java 17 is surprisingly simple. Most of the work is just upgrading libraries which use internal JDK features or rely on brittle reflection/bytecode hacks.

Java is a specifically strange beast here. With many modern typed languages, a major version upgrade will require you to modify small parts of your code, as the compiler becomes stricter or small parts of the syntax change.

Java is more like C and C++, in that it tries very hard to maintain language level compatibility. On the other, library compatibility is a bigger mess in Java, because the language was pretty stagnant for such a long time, and library authors got used to a very slow upgrade pace. This meant many libraries and tools chose to rely on internal JDK implementation details or perform bytecode wizardry which stopped working with new versions. Java 9 also moved a lot of interfaces which were not an official part of the Java SE spec (like all of Java EE) to external artifacts which you needed to add as dependencies.

Another extreme point of incompatibility was very silly: libraries were making assumptions parsing the Java version string: up until Java 8, the version string had the format "1.x", where x is the major version (Java 8 was "1.8"). Starting from Java 9, there was no phantom "1." prefix coming before the major version anymore, so Java 9 was just "9". This broke a lot of libraries which would have been compatible otherwise.

Library maintainers were unprepared for the rather small changes Java 9 has made, and took quite some time to upgrade, especially huge frameworks like Spring. It took Spring 6 months to support Java 9 after it became GA, but Java 17 was fully supported 3 months before it was released![1]

If you have to upgrade from Java 8 to Java 17 now, there is nearly zero friction if your libraries support it and upgrading the libraries is easy. You'd barely need to touch your own code. If any changes are forced on you, that's usually the libraries, but then neither Java nor static typing is to blame.

And honestly I think it's quite easy to upgrade. In my experience, Java shops just tend to be more conservative than the industry average.

[1] https://twitter.com/snicoll/status/1420652097373188100


Still need effort to move larger codebases No matter what people say..


Is that a Java issue, a typing issue, or a company issue?


Go 1.x is around 9.5 years old, and many Go developers write software using mostly the standard library without any framework.


> using mostly the standard library without any framework

I don't think that's totally true. Without any framework, maybe, but mostly the standard library? I'm not sure. People use a lot of libraries, and these may not have the same guarantees as Go itself.


Comparing language with the framwork, even if you want to better compare it with Sinatra then


In my experience, upgrading rails used to be messy (e.g. 2->3 was a blood bath) but recent version upgrades have been mostly straightforward.

It does help to stay on top of gen upgrades as much as possible, but I do not believe this is a rails specific thing.

Maybe useful: there are companies providing long term support for old rails versions.


> Do people allocate a certain amount of time just to upgrade Rails?

Rails spits a bunch of deprecation warnings when stuff is changing. Usually it's just a matter of playing whack-a-mole with the deprecation warnings and walking through the upgrade instructions. It's a chore but it's not especially difficult, even if you're multiple versions behind (in which case you go should upgrade through the intermediate versions one at a time).

The thing that will save your bacon here is having decent test coverage, so you actually see those deprecation warnings :)


> I have heard this many times from many different people that I respect. However, I'm just not experiencing "the joy" after several attempts. Maybe it's just not how my brain is wired.

This has been my exact experience too. My current theory as to why it doesn't click for me is that it tries to be too much like English. Sure it's fun writing poems all day but try refactoring a poem someone else wrote with any kind of confidence.

That said I love that ruby and rails exist, it seems to unlock creativity in some people and that's awesome.


A highpoint in my coding journey was drinking beer with a friend who know Rails, and we built 3 apps in two evenings. It was not just amazing that we could be so productive, but it was so much also fun! The fun as in discovering how Ruby helps you to solve your problems, and not to mention the ridiculous power of Rails. I want to go back.

Now I am like a Romulan, all I do is computational computer science with NumPy and Pandas.


I was a big Rails, Ruby and dynamic typing fanboy. But then my project grew in size and I changed my beliefs.

I'd not start a big project in any language without: null-safety, proper sum-types, type inference.

Hence I like Kotlin, and KTor seems to be a good Sinatra/Flask like in that arena.

Another interesting development I find no-code/low-code tools for the backend, like Hasura. This allows me to "just expose Postgres over GraphQL" with very little code (mainly configuration). That combined with type-safe client library generation for a typesafe frontend language like Elm gives me all the power I need in a very different paradigm. Something worth considering.

Small example Hasura+Elm project: https://github.com/cies/low-code-backend-dockered


> I'd not start a big project in any language without: null-safety, proper sum-types, type inference.

very interesting; I've been looking into sum-types recently, esp from a categortical perspective https://www.youtube.com/watch?v=2LJC-XD5Ffo

Maybe there is something about the necessity of Natural Transformation somewhere when the system scales to a certain size


I love Bartosz Milewski! Great talks to make cat theory understandable.

Sum-types to me are just a necessity for expressing certain situations that occur a lot. Enums do not cut it, and making sum-types from records is just ugly, waaaay to much boiler plate and hard to deal with in practice (one needs sum-types AND switch expressions + pattern matching to really unlock the bliss of sum-types).


More power to anyone and everyone who can find a language/framework/niche that’s enjoyable for them to work with. The feeling of smooth, frictionless, joyful flow is really something special.


It's early days but so far I've quite enjoyed my experience with Buffalo (Golang) framework [1], which mostly copies from Rails. Get Go performance and static typing. Definitely some rough patches, but overall still quite an enjoyable experience (so far).

[1] https://gobuffalo.io/en/


A lot of people say the same things about Rails. It makes you productive, it's incredibly easy to do things, things just work, etc.

I have used Rails for most of my career and for a number of side ventures. I have to say I have had a love-hate relationship with it.

I love everything people love about Rails (mentioned above). What I hate is how the speed and productivity comes back to bite you later.

The code eventually turns into a ball of mud and becomes near impossible to update. You have N-dependencies that depend on Y-dependencies of their own. Different Rails versions or module versions, and you can't update one without breaking something else.

You eventually live with it, and nothing gets updated but then you want security updates, and sometimes something terrible happens (like the mimemagic catastrophe) that forces you to ham fist updates together and live with some rails engines not working completely. But that's ok, because productivity right?


I would argue that the problems you mentioned are not unique to rails.

I've seen those same problems in other technologies and architectures, just as I've seen them in Rails applications. I think those issues come from lacks of carefully enforced standards, separation of concerns, domain design, and discipline. These are people problems, not technology problems.

What rails provides is the freedom to think about your business domains and such without having to also design the plumbing. I would argue that rails gives you space to solve those problems, but you still have to solve them.


There seem to be only 2 opinions of Ruby+Rails and neither of those opinions are "it's fine..."


Rails gives you so much out of the box that it's very tempting if your app lies in Rails' sweet spot.

However I've found that as apps grow bigger and accumulate more complex business logic and data flows Rails codebases become increasingly difficult to manage.


Bad news for you: as apps grow bigger, they become complex and difficult to maintain in every single platform or language.


This is likely the organization of the project. What do your service objects look like? Domains? Repositories? DTO's? How do you organize them?

How about adapters?

There's some great pieces of literature out there about advanced ruby / rails - well worth the read!


IMO it has mostly to do with lack of static type checking and null safety. I don't encounter the same issues in large codebases in languages like Swift and Typescript.


As someone who has started learning Ruby to contribute to one of my companies' repos and the amount of "magic" in Rails has been a big stumbling block for me compared to any other language I've worked in lately (JS/TS, PHP, Python).


One big problem with Rails code is that when you're looking at any particular block of code is very difficult to figure out the scope of variables. They can be coming from a base class, a mixin, some kind of plugin etc. And dev tools usually won't help you figure this out. This alone makes maintaining Rails code a lot harder than something like Node/TS where all your imports are explicit.


This I would definitely agree with. I’ve found this many times when jumping into a new repo to fix a bug.


100% agree.. If the services/jobs are focused, then even massive projects can be held in your head. And these can be built out if they're not there, so they can be the road to recovery


Agreed. I feel like a big part of the disconnect between Ruby/Rails enthusiasts and Ruby/Rails detractors comes down to what kinds of projects are being worked on. (This is a frequent theme in HN discussions of various technologies, not just Ruby/Rails.) For scrappy MVP's, where the most important thing is to quickly find product fit, Rails is often a great choice.

The problem comes when that codebase starts to mature: you look up one day and realize that it's no longer just you working in the codebase. The lack of language-level documentation makes it difficult to onboard new members to the team. A new developer is inspecting a method they're tasked with refactoring. It accepts an argument named "input". What the hell is an input? Is it a hash? Is it a class instance? Both? Something else entirely? Idiomatic Ruby/Rails makes it quite hard to intuit expected inputs/outputs. (Ironically the community also vocally disavows the use of comments, which would ameliorate that problem somewhat.)

Meanwhile, "Undefined method `blah` on nil:NilClass" errors start to litter your logs to the point that developers become numb to them. Invariant violations that should have been caught upstream, at their point of inception, sneak all over your runtime and you waste valuable employee hours trying to identify their true source.

And Ruby's poor story around async i/o ties your hands as your traffic patterns begin to require a more sophisticated request processing pattern than "receive request, block thread, return response." Phrases like "worker saturation" and "let's make it an async job" start to dominate your engineering retrospectives.

These are just a few of the sharp edges that come from trying to scale Ruby. You may point to solutions to them, but they don't compare in elegance to working in a language that obviate those problems in the first place. And while it's very easy to start a project in Ruby and gain a quick initial burst of momentum, it's commensurately difficult to switch to a more mature language+framework when you realize that your engineering org is suffering from Ruby/Rails shortcomings.

This is not a blanket indictment of Ruby/Rails -- just an acknowledgement that different languages and frameworks are better suited to certain types of problems. In my opinion, Ruby/Rails are very well suited for small projects with few maintainers, where the total surface area of the domain model is pretty small. Or quick MVP's where you have to deliver next week or die. But the dynamic duo are not well suited to the kinds of problems that start to emerge as those MVP's scale up in complexity/LOC/number of maintainers. It's a bit of a catch 22.

People will point to the big companies running Ruby/Rails. But from having worked at one of them (Shopify), I can say that we were spending an inordinate amount of time trying to plaster over their shortcomings; I'd imagine this is also true of the other big companies that run on the same stack.


I have to say that as a rails dev I've never found there to be a lack of documentation. Not if there's even a little bit of a PR process in place. Rails in general is a huge proponent of test, then code. The tests are the documentation. They tell you what params methods take, and what the output should be.

I'll admit I've never worked on a team over 100, but I have worked on older code bases.

When we found bugs, we wrote new tests to cover the edge case.

Also if someone had "input" as a variable name for a public method, and that made it into main, that's a failing of the PR process.

Most of the complaints here could be replicated in almost any language. In a c++ project I worked on in the 2000's I had a dev create a function void rct(int data). I (as lead) made him rename both the function and the param.


> A new developer is inspecting a method they're tasked with refactoring. It accepts an argument named "input". What the hell is an input?

This is more of a general criticism of dynamic typing than anything specific about Ruby, but If it's unclear from the code why is it so hard to put a binding.pry in there and inspect the object if you need to? You'd usually wanna put a breakpoint in there anyway when refactoring to see what the hell is going on. And on a normal codebase there will usually be an accompanying test.

I'm not 100% denying that in Java you'll have a type like FactoryBeanXmlConfig which you can go ahead and read the documentation to (which is often insanely verbose due to everything being a type in Java. So lots of reading).

To each his own I guess; I think we all get used to whatever stack we use and become very productive if we stick to it. I acknowledge I would've been productive in Java eventually to the same degree as I am with Ruby, I just wouldn't have enjoyed it as much.

The Shopify example is a bit extreme: you are talking about one of the biggest monoliths in the world; not strictly for Ruby, in any language. So yes you're gonna have to build some tools to accomodate that and yes, probably Java would have worked out better at that size. But how many companies become Shopify - 0.1%? It's either a "scrappy MVC" or Shopify? I disagree: Up to that size of millions of LOC and thousands of full time devs there's a whole spectrum of companies size where Ruby/Rails works out well. I can't see why a 50-100 man team can't work with Ruby successfully; if the monolith becomes a pain you break it into a few smaller monoliths/citadel/microservice. This essentially allows you to scale to any size, but I think the monolith will take you far; look at Gitlab. I'd say around 100 devs work on it full time (my guesstimate, could be wrong) and their feature development speed is brilliant imo. There are so many successful Ruby/Rails companies at those sizes it would feel silly to start naming them.


This is more of a general criticism of dynamic typing than anything specific about Ruby, but If it's unclear from the code why is it so hard to put a binding.pry in there and inspect the object if you need to?

This is a lot slower and more tedious than just inspecting the documentation for a statically typed parameter and the cost of doing it over and over and over again adds up. Also there's no guarantee that whatever type you observe in your particular binding.pry experiment is the only one the function receives.

Of course people continue to build large & successful apps in Ruby. I'm just saying at a certain point it gets harder than it would be in other languages.


> Also there's no guarantee that whatever type you observe in your particular binding.pry experiment is the only one the function receives.

I honestly don't see many methods where it expects a user but it can also receive a shipment or a billingInfo where tons of callers just pass all sorts of stuff to it as they feel like, and me as a dev have to keep wondering what the param is. That's not common at all because most Ruby devs just don't operate that way (Why would they? That's pretty insane). Yes it's easier to shoot yourself in the foot in Ruby but that doesn't mean we all design crap code.

Static type people say your dynamic code is always littered with dozens of if param.type == 'user' else if param.type == 'baboon' etc etc. I rarely stumble into this in real life. I'd like to see some real examples (there's enough python/js/ruby open source code for us to make an intelligent discussion)


> Yes it's easier to shoot yourself in the foot in Ruby but that doesn't mean we all design crap code.

As your team grows it becomes harder and harder to prevent people from shooting themselves (and the rest of the team) in the foot in such a way, doubly so when you're working in a language that more easily permits it. To your point, if you're working in a smaller team with collaborators that you trust not to make these mistakes, that's a different situation.

The inscrutable-method-input scenario that haunts me the most is the case of "options hashes" in Ruby/Rails. Someone writes a method that accepts a single hash of possible options keys, instead of an N-arity method that accepts those scalar values directly. When someone needs to modify the behaviour of that method, the short-term path of least resistance is to just throw another key onto the blob. In the most notorious example I can think of, one of our applications had an options hash with 30 (not an exaggeration) possible keys, after several years of developers deeming that the easiest way to implement whatever feature they were tasked with implementing. It was a total nightmare trying to debug situations where Key A | Key B conflicted in subtle ways with Key C & Key D.

Of course there's nothing preventing that sort of scenario from emerging in a statically typed codebase, but I find there's more friction around doing so. Especially if you start out with a rigid interface (with non-nullable keys) that all your callsites must conform to. If someone tries to add a new key to the argument interface, those callsites will need to be updated to match.


> Of course there's nothing preventing that sort of scenario from emerging in a statically typed codebase, but I find there's more friction around doing so

I actually agree with this, but there's also a bit more bloat and less flexibility. Also, I like the fact that Ruby/Rails is powerful and flexible and deserves trained hands. I kidna hate the Go philosophy of "code written by a junior and code written by a senior looks about the same". That's not for me. I wanna enjoy the progress I make in the language.


I don't have examples at hand but I've worked in large Swift, Typescript and Rails codebases over the last seven years and the Rails codebases were the hardest to maintain and had the most errors in production. Have you done extensive work in a strongly typed language?


Yes I did C/C++ and Java in college and kinda hated it. It wasn't web based development work like I do now but I've written a bunch of static code in my life. I also took Spring MVC for a spin more recently and absolutely hated it with a passion. I think that the Ruby/Rails project you describe was probably classified as "legacy" and the company moved on to newer cooler tech like Swift/Kotlin/Node. That's when the Rails project becomes really fun where no one wants to touch it/improve/refactor anything and it quickly becomes a pile of shit after a few years since every change is done by someone hating that project and hating Ruby. Am I close? Otherwise I can't really understand why a skilled group of Ruby devs couldn't have refactored the thing.


C++ and Java are not going to give you the experience of working in a modern statically typed language.

The issues I describe with Rails are issues I’ve seen across many different apps and teams.


Thanks for articulating more clearly what I was trying to say. My enthusiasm for Ruby diminished a lot when I first experienced the things you describe on a larger code base.

I have not found these issues occurring to anywhere the same extent in languages with stronger correctness guarantees.




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

Search: