Originally published on my old blog in October 2022.
Over the last few months, I’ve been learning Ruby on Rails and Hotwire, its default frontend framework. Rails has been both fascinating and frustrating and it has changed my assumptions about web app programming in surprising ways.
It’s been fascinating because I can go from an empty terminal to a working blog in 20 minutes. Since there's a default correct way to do things, you can focus on your app functionality right away, rather than file structuring or boilerplate. Rails has so much “magic” built-in that automates things that we’ve already decided how to do (RESTful routing, database management, etc). The point of conceptual compression is that you can compress and forget about the workings of some fundamentals to make room for newer concepts. It sounds like a cheat code at first because it kind of is. But not using conceptual compression means that you have keep an increasing number of abstractions in your head while adding new ones, all the way from how SQL works to responsive UIs. I feel like my ego was certainly involved here: if I’ve had to learn the basics of routing and relational databases, don’t I need to implement them from scratch to be a real programmer?
It’s this Rails magic that makes the programming experience strangely frustrating because it doesn’t feel like real coding, compared to writing React or vanilla JS. The idea that I’m playing with a toy and not the real thing was a result of so much work already being done for me. After a few starter projects, I realized that most backends do the same things: auth and a variation of CRUD on some models in a database. To that end, I found the Rails model and scaffold generators to be incredibly powerful — they did all the boilerplate for me, leaving the rest of the work customizing the backend to my app’s functionality.
That’s another misconception I had about Rails. That since it was so opinionated, there wasn’t any room to customize. That’s one of the first assumptions people have when I tell them I’m learning Rails. But this is actually the opposite of the truth: since all the boilerplate is done, the work you do is actually customizing the app to your liking.
A lot of my time was spent on ERB template files doing conditional rendering and modifying HTML and CSS. Maybe this is why writing Rails code didn’t feel like real coding. Most people wouldn’t consider HTML, CSS, and a couple if/else statements real coding; I think I missed writing the fancy React abstractions that made me feel like I was doing something challenging even when it was something basic, like a drag and drop app. In React, you have to think about imports, component structure, JSX, useEffects, and state all before you start writing your app-specific logic. With Rails, I found that adding a new feature often took around 10 lines and no additional abstractions to think about.
The most amazing part for me and to people I tell about Rails is the monolith structure. I’m not sure when frontend became entirely separated from backend, but I assumed that it had to be so to develop “modern” web applications with responsive frontends. When I was working with a medium sized React + Rails stack, there were three levels of truth: local state, Redux, and backend/db state. It was incredibly confusing when to update which one and when to send updates to the server while keeping frontend responsiveness. Wasn’t the whole point of React to have state be local and flow from top to bottom when needed?
In many ways, React has outstretched its original purpose. The whole point of SPAs was to have snappy applications that did a few things. I find it ironic that React is now used to build entire sites with routing and global state using a bunch of workarounds like Redux and React Router. People are even using React for server-side rendering (Next.js). Instead of thinking about how to best solve a problem, people try to figure out how to fit something into the React framework. What results is a string of incomprehensible code in a million different files, with a constant need to refactor.
Of course, there are situations where React is needed to do the job. But I now believe that for most web apps, it’s not. That’s what surprised me the most about using Rails: how I was able to get SPA-like functionality with the default Rails frontend framework. Hotwire or “HTML Over The Wire” lets you update partial components on the page, stream updates from the server via websocket, and write custom JS for specific events when needed. Server-side rendering made state management super easy: there was one source of truth and backend seamlessly works with frontend. No more managing two servers, frameworks, or deploys. I was able to get all of this functionality without having to think about the whole other dimension of virtual DOMs, re-renders, state management, and component lifecycles.
The feeling of play when programming with Rails may also be an advantage. Because when something doesn’t feel like serious work, you can pretty much do whatever you want. And that’s how many successful companies have been started: as a toy project that the founders worked on out of curiosity or by building something for themselves.
Another consideration to think about when it comes to Rails is the developer community and current trends. I always thought of Rails as this boomer framework till I actually learned about it and tried it. Literally all of my CS friends are React developers and they are so embedded in the JS ecosystem that it’s hard to explain that there might be a better way to do things. I’m still a little bit hesitant to leave React completely behind since so many companies still rely on it for their frontends.
But this might be the very reason to go forward with Rails. I was extremely skeptical at first but wanted to learn a backend language that a company I was interning for was using and that was behind the MVPs of some of my favorite companies like AirBnb, Square, and Github. Using an untrendy language that you’re very excited about and that you personally believe in is an advantage in a world where everyone is doing the same thing simply by default. I was amazed when I was able to build a full stack drag and drop app in a day in under 500 lines, fully deployed with a Postgres database, when it had taken my friend’s team over 2k frustrating lines in React just to get the front-end to work. If a company can do the same thing with fewer lines of code, in less time, and with fewer people, it will always be at an advantage.
So, becoming an expert on Rails and Hotwire will mean feeling left out of the JS framework-of-the-week. In the end, all that matters is what you’re able to build and how fast you’re able to do it. And my experience with Rails and Hotwire over the last few months has made me a die-hard Rails fan, something that I couldn’t have predicted when I first started learning web dev via JS and React two years ago.
Over the last few months, I’ve been learning Ruby on Rails and Hotwire, its default frontend framework. Rails has been both fascinating and frustrating and it has changed my assumptions about web app programming in surprising ways.
It’s been fascinating because I can go from an empty terminal to a working blog in 20 minutes. Since there's a default correct way to do things, you can focus on your app functionality right away, rather than file structuring or boilerplate. Rails has so much “magic” built-in that automates things that we’ve already decided how to do (RESTful routing, database management, etc). The point of conceptual compression is that you can compress and forget about the workings of some fundamentals to make room for newer concepts. It sounds like a cheat code at first because it kind of is. But not using conceptual compression means that you have keep an increasing number of abstractions in your head while adding new ones, all the way from how SQL works to responsive UIs. I feel like my ego was certainly involved here: if I’ve had to learn the basics of routing and relational databases, don’t I need to implement them from scratch to be a real programmer?
It’s this Rails magic that makes the programming experience strangely frustrating because it doesn’t feel like real coding, compared to writing React or vanilla JS. The idea that I’m playing with a toy and not the real thing was a result of so much work already being done for me. After a few starter projects, I realized that most backends do the same things: auth and a variation of CRUD on some models in a database. To that end, I found the Rails model and scaffold generators to be incredibly powerful — they did all the boilerplate for me, leaving the rest of the work customizing the backend to my app’s functionality.
That’s another misconception I had about Rails. That since it was so opinionated, there wasn’t any room to customize. That’s one of the first assumptions people have when I tell them I’m learning Rails. But this is actually the opposite of the truth: since all the boilerplate is done, the work you do is actually customizing the app to your liking.
A lot of my time was spent on ERB template files doing conditional rendering and modifying HTML and CSS. Maybe this is why writing Rails code didn’t feel like real coding. Most people wouldn’t consider HTML, CSS, and a couple if/else statements real coding; I think I missed writing the fancy React abstractions that made me feel like I was doing something challenging even when it was something basic, like a drag and drop app. In React, you have to think about imports, component structure, JSX, useEffects, and state all before you start writing your app-specific logic. With Rails, I found that adding a new feature often took around 10 lines and no additional abstractions to think about.
The most amazing part for me and to people I tell about Rails is the monolith structure. I’m not sure when frontend became entirely separated from backend, but I assumed that it had to be so to develop “modern” web applications with responsive frontends. When I was working with a medium sized React + Rails stack, there were three levels of truth: local state, Redux, and backend/db state. It was incredibly confusing when to update which one and when to send updates to the server while keeping frontend responsiveness. Wasn’t the whole point of React to have state be local and flow from top to bottom when needed?
In many ways, React has outstretched its original purpose. The whole point of SPAs was to have snappy applications that did a few things. I find it ironic that React is now used to build entire sites with routing and global state using a bunch of workarounds like Redux and React Router. People are even using React for server-side rendering (Next.js). Instead of thinking about how to best solve a problem, people try to figure out how to fit something into the React framework. What results is a string of incomprehensible code in a million different files, with a constant need to refactor.
Of course, there are situations where React is needed to do the job. But I now believe that for most web apps, it’s not. That’s what surprised me the most about using Rails: how I was able to get SPA-like functionality with the default Rails frontend framework. Hotwire or “HTML Over The Wire” lets you update partial components on the page, stream updates from the server via websocket, and write custom JS for specific events when needed. Server-side rendering made state management super easy: there was one source of truth and backend seamlessly works with frontend. No more managing two servers, frameworks, or deploys. I was able to get all of this functionality without having to think about the whole other dimension of virtual DOMs, re-renders, state management, and component lifecycles.
The feeling of play when programming with Rails may also be an advantage. Because when something doesn’t feel like serious work, you can pretty much do whatever you want. And that’s how many successful companies have been started: as a toy project that the founders worked on out of curiosity or by building something for themselves.
Another consideration to think about when it comes to Rails is the developer community and current trends. I always thought of Rails as this boomer framework till I actually learned about it and tried it. Literally all of my CS friends are React developers and they are so embedded in the JS ecosystem that it’s hard to explain that there might be a better way to do things. I’m still a little bit hesitant to leave React completely behind since so many companies still rely on it for their frontends.
But this might be the very reason to go forward with Rails. I was extremely skeptical at first but wanted to learn a backend language that a company I was interning for was using and that was behind the MVPs of some of my favorite companies like AirBnb, Square, and Github. Using an untrendy language that you’re very excited about and that you personally believe in is an advantage in a world where everyone is doing the same thing simply by default. I was amazed when I was able to build a full stack drag and drop app in a day in under 500 lines, fully deployed with a Postgres database, when it had taken my friend’s team over 2k frustrating lines in React just to get the front-end to work. If a company can do the same thing with fewer lines of code, in less time, and with fewer people, it will always be at an advantage.
So, becoming an expert on Rails and Hotwire will mean feeling left out of the JS framework-of-the-week. In the end, all that matters is what you’re able to build and how fast you’re able to do it. And my experience with Rails and Hotwire over the last few months has made me a die-hard Rails fan, something that I couldn’t have predicted when I first started learning web dev via JS and React two years ago.