Are we missing primitives? Briefly comparing Rails & Laravel out of box experiences

Hi All,

In a previous post I did a bit of a comparison that was looking at the differences between Rails and Java’s Spring framework when it came to bootstrapping a new application in order to provide a “batteries included” kind of experience.

I made a note there that I hadn’t really looked at other comparable solutions in other frameworks but that I thought it might be worth exploring to see what efforts might make sense to bring to Rails land.

Starting Premise of My Post

  1. Many developers (myself included) are lacking guidance about how to grow “real world apps” with just a purely MVC approach. That is to say basically nothing of the split for many between front and back ends (i.e. SPAs etc)
  2. There are large subsets of functionality that are extremely common in most applications for which we don’t as yet have a “Rails way” of approaching so much as we have “a handful of popular gems”.
  3. We have collected 10+ years in many cases of data from real world solutions as to how well those existing solutions and patterns work and as such we might want to think about any “grand unifying theories” to these problems that keep popping up (i.e. authentication, authorization and events as a small number of examples).
  4. It is the role of a framework like Rails to provide developers with what I would probably describe as really well crafted building blocks which can be put together in order to build an application while still acknowledging that in some cases people have very specific needs and allowing them to bring their own solutions if needed.
  5. By comparing and contrasting Rails internal frameworks and primitives against those of other comparable ones we can not only begin to identify where some of those gaps might be and discuss the relative pros and cons of their approaches.

My Argument

The joy of Rails is ultimately underpinned by 3 main things:

  1. Ruby is a really expressive language and is generally a pleasure to code in.
  2. Rails provides a good “out of box” experience precisely because it has a set of really well crafted primitives that abstract away a number of hard parts of web development.
  3. The combination of these two things are what makes it such a good choice if you want to deliver real world applications in a short amount of time.

I’m making the argument that this is possibly under threat precisely because we are missing “the Rails way” to a whole bunch of problems and everyone is kind of doing their own thing at the moment and it I get the feeling that it is a bit of a mess once an application reaches a certain size.

I saw a tweet last night from Adam Wathan (creator of Tailwind CSS) that said:

The only problem with Laravel is that you have to subject yourself to writing PHP to use it It’s better enough to be worth it though in my opinion.

I think that is a claim worth at least exploring and discussing both as a community and maybe to get some further thoughts from Rails core team in particular.

Let’s start by looking at Laravel’s /app folder structure

The /app folder inside a Laravel project is very similar to a Rails one in the sense that it is designed to handle all of the core code for your application and almost all of your classes for an application would live here.

Note: I’ll start by saying that I am almost certainly going to do a crappy job of covering this properly and you should take a look at the official docs for this here.

However, you have the following sub-directories to help you organize your code:

  • Broadcasting: This folder is for code relating to the concept of broadcasting events via WebSockets and I guess would be roughly equivalent to the channels folder in Rails.
  • Console: Code for running various commands and scheduled jobs in a way that looks similar to Rake tasks
  • Events: Events are a good example of a pattern which is extremely common in modern web applications across comparable frameworks but is missing from Rails. To be fair that isn’t technically true because while we do have gems like whisper for example and we also have this recent change which took ActiveSupport::Notifications and made them suitable for a general event system. However, I would make the argument that many people seem to be unaware of it as it was historically used for a different purpose and that we might still be missing some Rails magic on top yet.
  • Exceptions: A folder for creating custom exceptions and any code associated with how exceptions are logged, rendered and generally handled.
  • Http: This is as far as I can tell more or less the same as app/controllers in Rails land.
  • Jobs: Think Active Jobs
  • Listeners: This is basically the other half to the concept of events. Events in turn have Listeners which will run in response to any Events that piece of code is registered to Listen for.
  • Mail: Think Action Mailer
  • Notifications: This is an interesting abstraction from Laravel. Think of Notifications as any kind of transactional notifications that are sent by your application. Laravel’s notification features abstracts sending notifications over a variety of drivers such as email, Slack, SMS, or stored in a database.
  • Policies: This folder contains all of the authorization policy classes for your application. Right now this is another good example of something where we have a number of competing solutions to this in the Rails world right now but no “official way” despite the fact that most applications need this. For the record I think a project like Action Policy might be a good starting point for that conversation.
  • Providers: Laravel has the concept of Service Providers which is described in the docs as: Service providers bootstrap your application by binding services in the service container, registering events, or performing any other tasks to prepare your application for incoming requests..
  • Rules: Rules are used to encapsulate complicated validation logic in a simple object.

So already I can start to see some interesting patterns and abstractions emerge. Of particular note to me is:

  1. Events & Listeners
  2. Policies
  3. Complex validation through Rules

Other Interesting Concepts / Primitives & Frameworks

At the risk of making this post impenetrably long I am just going to leave this section as a extremely brief whirlwind that is just designed to serve as a starting point for conversation and potential inspiration.

Service Providers

Service providers are the central place of all Laravel application bootstrapping. Your own application, as well as all of Laravel’s core services are bootstrapped via service providers.

But, what do we mean by “bootstrapped”? In general, we mean registering things, including registering service container bindings, event listeners, middleware, and even routes. Service providers are the central place to configure your application.

Service Containers

The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are “injected” into the class via the constructor or, in some cases, “setter” methods.

Facades

Facades provide a “static” interface to classes that are available in the application’s service container. Laravel ships with many facades which provide access to almost all of Laravel’s features. Laravel facades serve as “static proxies” to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.

Contracts

Laravel’s Contracts are a set of interfaces that define the core services provided by the framework. For example, a Illuminate\Contracts\Queue\Queue contract defines the methods needed for queueing jobs, while the Illuminate\Contracts\Mail\Mailer contract defines the methods needed for sending e-mail.

Each contract has a corresponding implementation provided by the framework. For example, Laravel provides a queue implementation with a variety of drivers, and a mailer implementation that is powered by SwiftMailer.

Potential Points for Discussion

  1. Is there anything from the list above that inspires a strong feeling either way that you would or wouldn’t like to see?
  2. For those of us with some level of experience in other frameworks what are the things you miss most when having to work in Rails?
  3. As I mentioned in my previous post, I don’t work professionally as a developer at all and as such I feel like there is a strong potential for a big disconnect between the points I raised in the premise section above and the wider community. I’d love to hear peoples thoughts on that.

Finally, I haven’t covered this at all in this post because I got the impression that some things are very clearly in the “out of scope” category but Laravel has a whole bunch of really polished looking solutions for things like Deployment, Multitenancy, Customer Billing, Authentication and Administration.

However, I am extremely curious to hear what people think about this and where they see Rails continuing to fit into the potential list of choices for the kinds of people who have traditionally made up the core audience of Rails (i.e. People who have an idea that they want to turn into a code and potentially a business in a quick and sane manner).

I have an entire argument that goes roughly along the lines of:

  • Rails has traditionally not had much in the way of strong competition for people with those kinds of goals as as such it has been easy to ignore. However, that is no longer true and it’s not even obvious that Rails is even the best choice right now for developers in a situation like that.
  • There is a huge amount of potential real world impact (primarily through upwards social mobility and the general ability to deliver solid technical solutions to common problems) that could be realised if we made those kinds of tools available to the Rails community.
  • Every time an otherwise good project never ships because people ended up getting caught up in these kinds of topics it is a tragedy and we should be thinking about how we as a community can ensure that happens less often.

I would obviously love to hear peoples thoughts about that also and if you made it this far please accept my very sincere thanks for your time, I know this was a bit of a monster post :slight_smile:

7 Likes

Thanks for the detailed write-up. Answering point 1, as regards my own app I don’t feel like I am critically missing most primitives mentioned in this post, as after all my app and many others work without them and I feel the Rails ecosystem has its own way of answering those needs.

That being said, there are things which would be amazing to have in Rails core, such as admin dashboards. I use Administrate which is great but I would love to somehow see a bigger community behind such a project. As mentioned in another thread, a debug interface like Telescope would be great too.

1 Like

The thing I love about Ruby (and Rails) is that it allows me to write isolated simple small solutions to each problem I face incrementally. Eg. if my app only has normal users and 1 admin view, the simplest solution would be to gate that view beind a if user.admin? flag.

Over time, there are areas where the interaction of simple small solutions start to get complex and hard to change. That’s an indication that that area needs a review. That review might lead to a larger, more structured solution. Or maybe just a different, simple solution.

Sometimes, if the area calls for a larger, structured solution, I might reach for a gem which offers an acceptable approach (eg. Devise). This arrangement feels fine to me. If anything would help the current state of affairs, it’d be more people contributing and voting on Ruby based gem projects.

3 Likes

Honestly, when I see a main directory of that size, I start being reminded of http://trailblazer.to/, which I have had bad experiences with. Too many buckets and people start obsessing over which bucket something goes in, rather than how to adapt the buckets you have to the business domain of your application.

A lot of the things that you’re mentioning as Laravel-specific here are things that Rails does support – its concept of providers is roughly similar to Railties, its event/listener framework is similar to ActiveSupport::Notification, its “rules” framework is similar to Rails custom validators…

So it sounds like the actual question is, when and how is it appropriate for a framework to surface all of its features and all of its complexity? I like that Rails has a strong central MVC pattern and that I don’t need to think about all of its many options most of the time. But I totally get that because they’re not upfront they seem to be invisible to many developers in ways that may make Rails seem less fully-featured than it is.

3 Likes

I think the concept of events is a bit overloaded here. There’s the concept of events where an action fires an event which any other part of the system can subscribe to and handle. ActiveSupport::Notification can handle this (feels like I’m reaching into Rails’ internal tooling to hack my own solution for some weird reason)

And then there’s event sourcing where every change to an entity is additively captured and replayed to generate the latest state of the entity. This is a design pattern which some people advocate, along with others (service objects, functional programming, domain driven design etc.). IMO, applying this design pattern this is beyond the scope of Rails. However, it seems that people are looking to Rails for guidance on these patterns.

Think it’s not obvious that a feature that Rails has = solution for my problem. I used to roll my own Ruby objects, define their attributes, convert input into the correct type, etc. Then I learned about ActiveModel and a lightbulb went on.