or; Using CRUD as an abstraction
When thinking about a new web application I like to try to think about the URLs that my application has. The URLs are the bits that expose my application to the outside world, they define what my application can do, so thinking about them early on means that I'm focusing on one of the most important parts of my system.
As I develop complex web systems I've found that creating an organised, consistent URL system has significant benefits to my codebase overall, and makes my systems easier to create and maintain. At the same time I notice that beginning developers often create overly complicated and disorganised URL structures for their applications.
While this post is primarily focused on Rails, the main lessons here apply to any web application.
Consistency of Routing
We want to be able to get an idea of what a URL route does just by looking at it. We'd also like it if new developers could understand what is happening without much learning. For example:
GET /blogs/new - is the page where the form for a new blog is created. In the same app:
GET /products/new is the page where the form for a new product is located. These routes should NOT be:
GET /newblog or
In Rails these consistent routes are called "resourceful" routes. The important lessons here are that the naming structure is easy to understand, and that HTTP methods are often used to distinguish different things. For instance if you are performing an action which will save a record to the database, the
POST HTTP Method is the appropriate method to use. If you're updating an existing record then use either
The Rails routing guide has an excellent example of these types of routes.
|HTTP Verb||Path||Controller#Action||Used for|
|GET||/photos||photos#index||display a list of all photos|
|GET||/photos/new||photos#new||return an HTML form for creating a new photo|
|POST||/photos||photos#create||create a new photo|
|GET||/photos/:id||photos#show||display a specific photo|
|GET||/photos/:id/edit||photos#edit||return an HTML form for editing a photo|
|PATCH/PUT||/photos/:id||photos#update||update a specific photo|
|DELETE||/photos/:id||photos#destroy||delete a specific photo|
We can easily come up with some anti-patterns, or examples of "bad" routes:
|HTTP Verb||Path||Used for||Notes|
|GET||/addphoto||return an HTML form for creating a new photo||is inconsistent|
|POST||/photos/:id?update=true||update a specific photo||doesn't nest controllers appropriately|
|GET||/photos/:id?delete=true||delete a specific photo||uses GET to destroy a resource|
Rails also lets a developer add a "non-resourceful" route to a controller. In my opinion these are a serious code-smell and should never be used by beginning programmers. An example of this might be:
GET /users/search UsersController#search
While this type of route may seem easier than creating a new controller just to handle searching, in the near future it becomes very unwieldy to mix the concerns of searching with the concerns of the user controller. Some parts of the users controller might need to be heavily authenticated, while we might allow searching to be done by guests. As we add this logic to our applications and add extra "non-standard" actions to our controllers our apps become more difficult to document, modify and maintain.
Another example of this might be:
POST /jobs/restart JobsController#restart
In this example we're restarting some sort of job. This is simply being added to the
JobsController which is now in danger of becoming overburdened with functionality to do with the scheduling and control of our jobs. Also it seems likely that a job restart action might need some additional auditing and logging, which would further complicate our
JobsController. Instead of this what we're actually doing is CREATING a
JobRestart. By thinking about this functionality in terms of CRUD we're easily able to see that there is a new resource here which needs a new controller. This should be rewritten as:
POST /jobs/:id/restarts JobRestartsController#create
My preference is to never write non-resourceful routes and my recommendation to starting developers is to never use them either.
This rule has some additional benefits that might not be readily apparent. By forcing us to use "resourceful" routes we begin to think about all of our application in terms of create, read, update and delete or CRUD. This puts us in familiar teritory. I'll run through some examples:
1.) You are building a SaaS application and want to allow a user to invite another user into the system. You might be tempted to add an
invite route to the
UsersController but instead you should create an
InvitesController and use the
create method. This requirement is to create an invite so it's very appropriate to follow this pattern.
2.) You're building a song playlist management application and your boss wants you to make a "vote to skip" functionality. You could add
SongsController#skip, but it's far more consistent to add a new controller which handles the creation of the
SkipVote model. You might call this
SkipVotesController#create. If you had simply added the
#skip method to the
SongsController you'd be stuck if your boss later asked you to allow users (or an admin) to view the votes to skip various songs.
In some rare circumstances it will be difficult to think about your problem domain in terms of CRUD, but I believe it pays off in the long run. Naming things can be difficult, but working with legacy code where no care was made to name URLs and routes is a far worse position to be in.
No app left behind: Upgrade your application to Ruby 3.0 and s...
A look forward from 2020
Testing Rails applications on real mobile devices (both design...
reinteractive is Australia’s largest dedicated Ruby on Rails development company. We don’t cut corners and we know what we are doing.
We are an organisation made up of amazing individuals and we take pride in our team. We are 100% remote work enabling us to choose the best talent no matter which part of the country they live in. reinteractive is dedicated to making it a great place for any developer to work.
Webinars are our online portal for tips, tricks and lessons learned in everything we do. Make the most of this free resource to help you become a better developer.
The Ruby on Rails Installfest includes a full setup of your development environment and step-by-step instructions on how to build your first app hosted on Heroku. Over 1,800 attendees to date and counting.
The Ruby on Rails Development Hub is a monthly event where you will get the chance to spend time with our team and others in the community to improve and hone your Ruby on Rails skills.