I'd like to share a problem that I've seen play out on multiple teams at multiple companies. It presents as a problematic gem version locking us to outdated dependencies, though the underlying cause is violating principles of the Rails doctrine.
The doctrine
I've come into the Rails community comparatively late in the game. Rails is a mature stack. I got to skip over a lot of challenges, upgrading issues, and growing pains, as Rails has ever evolved into a tightly integrated tool. Something that took me an almost embarrassingly long time to stumble across is the Rails Doctrine.
If you missed it too, it's good reading. I want to point you toward one chapter of the doctrine: The menu is omakase. It's a short chapter, but it opens by saying:
How do you know what to order in a restaurant when you don’t know what’s good? Well, if you let the chef choose,y ou can probably assume a good meal, even before you know what “good” is. That is omakase. A way to eat well that requires you neither be an expert in the cuisine nor blessed with blind luck at picking in the dark.
Coming to the Rails world, I have often been thrown into projects that liberally violate the spirit of "Rails is omakase".Projects that do a highly customized version of Rails. Often that includes RSpec, FactoryBot, and Sidekiq. Some projects still carry technical debt that locks us into old tools because those were a good way to do things a number of years ago. Part of the spirit of omakase is that substitutions are possible. We're not totally dependent on the Rails Gods for everything. We can still flex some developer individuality.
Let's put a pin in that idea for a moment because I also want to point you to the doctrine section, Progress over stability.
Two points for your consideration:
Is Rails going to be better off for adopting another problem domain, like job queuing or WebSockets, in years to come? If yes, then let’s suck it up and do the work.
Rails should be at the frontier of helping Ruby’s progress by driving its constituents to adopt later versions faster.
We didn't always have ActionJob
or ActiveStorage
, so teams made it work with other tools. As Rails matures as a framework, it keeps incorporating more services into its domain. We no longer need gems like Paperclip
to handle file storage via ActiveRecord. Rails gives us that for free now, but the nature of technical debt means lots of apps still lean on these enabler gems.
Time to turn to my warning about "Glue Gems."
The problem with eating paste
What happens when we combine the spirit of Rails is omakase and Progress over stability? Frankly, there is a recipe for pain if we're not disciplined about the freedom we have.
When we lean on common substitutes sparingly, we can get effective tools that serve our domain or align with other doctrine principles like "Optimize for programmer happiness". Maybe your team has a real preference for RSpec, which is a really common practice on the projects I've had the pleasure of joining.If your only substitution is RSpec in place of Minitest, you'll be okay.
We should be honest, though. Is that really the only substitute happening on our projects?
Even if it's not, if we make isolated substitutions, we still err on the correct side of our omakase principle.Maybe we substitute RSpec for testing and also use the simple_form
gem to replace building forms, but the rest is out of the box Rails. Those two additions don't cross-cut the core concerns of Rails, and they're popular enough that we can probably stomach depending on them as Rails progresses into new versions.
The problems happen when we try to replace a whole course from our omakase meal.Front-end people love React, and boot camps love teaching React devs, so maybe we decide to carveout the view layer from Rails, bundle up a bunch of JavaScript with webpacker, and get React to do the view stuff.
If you've done this, you're not alone! I've seen a lot of projects that have made this massive substitution in a variety of ways. All of them lean on gems like:
I don't mean to throw shade at anyone who has done work on these gems. If that's you, you've provided me with a pathway into Rails from my React boot camp experience, and I am very thankful you've done so. Without you, I would still believe React is the best tool for everything, forever.
We should recognize the upside these gems provide. These glue gems can benefit small startup teams conscious of their deployment/server expenses or teams that need the extra ability to staff their engineering teams with React devs that can cross-train into Ruby/Rails work over time. Or, conversely, a pathway for a Rails-dominated team to explore a service leveraging tools from the React ecosystem.
Unfortunately, this isn't a happy story.
The move to Ruby 3 / Rails 7, with its changes to the default wayJavaScript is served, has put a bump in the road for projects leaning on a glue gem that pairs with webpacker. As of today, the path to upgrading to Rails 7 is rocky at best, but potentially impossible for some projects.
There's been movement on the react-rails
gem, tying it to shakapacker
, the official fork from webpacker
. Projects on this stack are looking at a dependency on the Rails-6-y webpacker approach to the asset pipeline and on a newly forked webpacker version. The organization managing shakapacker
is taking ownership of the react-rails gem as a way to renew investment into the tool. react-rails
sorely needs this type of investment to remain relevant.
I imagine you didn't anticipate facing Rails reorienting its relationship with JavaScript, handcuffing you to an old Rails version, and needing to use a particular JavaScript bundler because of a glue gem.
Again, you're not alone. I've seen this on more projects than I'd have expected, from many different teams.
The danger of the glue gem is that sometimes we're forced to eat paste. I mean, it's never really a fun time making major version upgrades in our applications. We all have to pay our technical debt in some fashion. There are problems upgrading within the omakase stack, but the number of people solving those problems is higher. There's safety in numbers.
Unfortunately, if we've gone down a glue path like react-rails
, it can feel like we're on a deserted island. The world of Rails is moving on, and we need to find a way to keep moving along with it, or we risk slipping into the End of Life death spiral when Ruby 2.7 goes away in March 2023. One day Rails 6.1 will follow.
We've fallen victim to violating Rails doctrine by relying on substitutions that have gotten in the way of making progress quickly.
Unwinding from the tangle
Getting out of the glue is tough and forces us to take ownership of our technical debt if we want to get off the desert island. Any direction to safety is going to have pain points. Without knowing what's happening on your app, I can't give you a perfect silver bullet. But I want the pain we feel to serve as a preventative warning for anyone thinking about using glue gems.
To get out of this dependency, we have to take complete stock of our application. How and where are we utilizing React?
If our views are all in on React, moving towards jsbundling-rails with webpacker/shakapaker
might make sense. It might also be time to pull the plug on the full-stack Rails/React app and move to a standalone frontend.
If we've peppered some React into our Rails views, migrating towards importmap-rails might be the way to untying us from the react-rails
glue. Maybe it makes sense to start reabsorbing a lot more of that functionality into core Rails. Things like leaning on default form builders glue. Maybe it makes sense to start reabsorbing a lot more of that functionality into core Rails.
Either way, just imagine the amount of work it's going to require to unburden our app and to stop eating the glue.
The hidden option is to continue down this pathway of pain.
Surviving through these gem changes might be a temporary affair. It is entirely possible Rails serving of JS is stable from here out, the maintainers of these glue gems pull through, and you get back on track.
That hope requires us to trust the stability of Rails and React. We haven't even discussed if you are okay with the shackapacker
dependency. Again, no shade at the people investing time and energy in these tools.I'm glad you're out here, and I appreciate you.
If you lean on these gems, you are at the behest of a small team working to maintain them.With Rails changing direction, there is a smaller circle of developers making these tools work. If you don't have the time to contribute to them, the whole business model of your company is absorbing a large risk on the back of the technical debt you're incurring.
If the money people have zero appetite for open source contribution, it's time for some tough conversations about paying down technical debt, getting closer to the omakase Rails stack, or investing in breaking out the front end to a standalone service.
Better yet, let's not start with these dependencies. Making good decisions for the health of our company starts by staying with the crowd until our domain complexity forces us out into the wilderness.
We can't save our past selves, but we can warn the people coming after us, so they don't have to feel the same pain.
Doing things smoothly
Maybe you're a team looking for a way to best leverage your resources. In that case, I'd encourage you to be very mindful about which gems you start to depend on. Consider which concerns they cross-cut. Can you defer choosing to use that gem? Can you dig deeper into the omakase Rails stack and solve your problem with the tools at hand?Is this just a glue gem that makes a tool work with Rails?
Tread cautiously. Be suspicious about what gems you let through the door. Our open-source community is amazing, and we all need to step up and contribute to these tools.
If we don't, or can't, then you never know when you are going to be left behind.
Did this resonate with you?
Are you on the desert island, stuck without a glue gem, with the world moving on? Do you have no idea where to start to get back to safety? Test Double would love to help—don't be shy about reaching out.
I have the joy of working with a bunch of thoughtful, empathetic, and focused developers who love helping the teams we work with get traction. We can take a look together, and make a plan for getting your project back in alignment with the Rails doctrine and blaze a path back toward the safety of the crowd.