A tiresome but essential aspect of managing large-scale applications is keeping track of cruftsuccinctly defined by Wikipedia as

anything that is left over, redundant and getting in the way.

Ruby on Rails applications are naturally no exception to this principle. Let’s take two of the most elemental components of a Rails app: routes and actions.

Rails will not explicitly warn you if a route is added that has no corresponding action, or vice versa. Instead it will simply allow your app to accumulate cruft. Although trying to access such an action or route will immediately throw a RoutingError or return a 404 response, this does not help to avoid the number of unnecessary routes and actions from increasing over time.

A disciplined approach to development and careful code review can help to avoid this fate, but the larger an application gets—and the more developers that work on it—it becomes ever more likely that superfluous routes or actions will be collected.

This is where another gem that I released in 2017, routing_report, comes in.

The gem adds a single Rake task to your Rails application. When you run the task, a report is generated that highlights both routes that have no reciprocal actions, and actions that have no corresponding routes.

To install the gem, just add it to your Gemfile:

group :development do
  gem 'routing_report'
end

And discover unwanted routes and actions by running the Rake task:

rake routing_report:run

Here is the report for the popular open source Rails app GitLab:

+------------------------------------------------------------------------------+
| Routes without actions (18)                                                  |
+------------------------------------------------------------------------------+
| doorkeeper/token_info#show                                                   |
| doorkeeper/tokens#create                                                     |
| doorkeeper/tokens#revoke                                                     |
| oauth/applications#edit                                                      |
| oauth/applications#show                                                      |
| oauth/authorizations#show                                                    |
| projects/badges#index                                                        |
| projects/boards#create                                                       |
| projects/boards#destroy                                                      |
| projects/boards#update                                                       |
| projects/boards#update                                                       |
| projects/branches#new                                                        |
| projects/merge_requests#diff_for_path                                        |
| projects/milestones#sort_issues                                              |
| projects/milestones#sort_merge_requests                                      |
| projects/services#index                                                      |
| projects/tags#new                                                            |
| snippets/notes#delete_attachment                                             |
+------------------------------------------------------------------------------+

+------------------------------------------------------------------------------+
| Actions without routes (57)                                                  |
+------------------------------------------------------------------------------+
| application#not_found                                                        |
| application#redirect_back_or_default                                         |
| devise#_prefixes                                                             |
| devise/confirmations#create                                                  |
| devise/confirmations#new                                                     |
| devise/confirmations#show                                                    |
| devise/omniauth_callbacks#failure                                            |
| devise/omniauth_callbacks#passthru                                           |
| devise/passwords#create                                                      |
| devise/passwords#edit                                                        |
| devise/passwords#new                                                         |
| devise/passwords#update                                                      |
| devise/registrations#cancel                                                  |
| devise/registrations#create                                                  |
| devise/registrations#destroy                                                 |
| devise/registrations#edit                                                    |
| devise/registrations#new                                                     |
| devise/registrations#update                                                  |
| devise/sessions#create                                                       |
| devise/sessions#destroy                                                      |
| devise/sessions#new                                                          |
| doorkeeper/applications#create                                               |
| doorkeeper/applications#destroy                                              |
| doorkeeper/applications#index                                                |
| doorkeeper/applications#new                                                  |
| doorkeeper/applications#update                                               |
| doorkeeper/authorizations#create                                             |
| doorkeeper/authorizations#destroy                                            |
| doorkeeper/authorizations#new                                                |
| doorkeeper/authorized_applications#destroy                                   |
| doorkeeper/authorized_applications#index                                     |
| omniauth_callbacks#authentiq                                                 |
| omniauth_callbacks#cas3                                                      |
| omniauth_callbacks#failure_message                                           |
| omniauth_callbacks#ldap                                                      |
| omniauth_callbacks#saml                                                      |
| profiles/notifications#user_params                                           |
| projects/branches#recent                                                     |
| projects/git_http_client#actor                                               |
| projects/git_http_client#authenticated_user                                  |
| projects/git_http_client#authentication_abilities                            |
| projects/git_http_client#authentication_result                               |
| projects/git_http_client#redirected_path                                     |
| projects/git_http_client#user                                                |
| projects/merge_requests/conflicts#authorize_can_resolve_conflicts!           |
| projects/network#assign_commit                                               |
| projects/protected_refs#create                                               |
| projects/protected_refs#destroy                                              |
| projects/protected_refs#index                                                |
| projects/protected_refs#show                                                 |
| projects/protected_refs#update                                               |
| sherlock/application#find_transaction                                        |
| sherlock/file_samples#show                                                   |
| sherlock/queries#show                                                        |
| sherlock/transactions#destroy_all                                            |
| sherlock/transactions#index                                                  |
| sherlock/transactions#show                                                   |
+------------------------------------------------------------------------------+

The results aren’t foolproof yet—for example, gems like Devise and Doorkeeper that satisfy routes in unconventional ways tend to generate false positives. This would need to be improved before the gem could be extended to be used programatically, for example as part of an continuous integration flow. (Pull requests are welcome by the way!)

But for manually highlighting redundant actions and dispensable routes the gem is already a useful tool.

You can find it on GitHub here: https://github.com/rfwatson/routing_report.