13 min read

Buggy Rails Code: The 10 Most Common Mistakes That Rails Developers Make

View all articles

Ruby on Rails (“Rails”) is a popular open source framework, based on the Ruby programming language that strives to simplify and streamline the web application development process.

Rails is built on the principle of convention over configuration. Simply put, this means that, by default, Rails assumes that its expert developers will follow “standard” best practice conventions (for things like naming, code structure, and so on) and, if you do, things will work for you “auto-magically” without your needing to specify these details. While this paradigm has its advantages, it is also not without its pitfalls. Most notably, the “magic” that happens behind the scenes in the framework can sometimes lead to headfakes, confusion, and “what the heck is going on?” types of problems. It can also have undesirable ramifications with regard to security and performance.

Accordingly, while Rails is easy to use, it is also not hard to misuse. This tutorial looks at 10 common Rails problems, including how to avoid them and the issues that they cause.

Rails best practices like the ones in this tutorial help you avoid Rails mistakes in your code.

Common Mistake #1: Putting too much logic in the controller

Rails is based on an MVC architecture. In the Rails community, we’ve been talking about fat model, skinny controller for a while now, yet several recent Rails applications I’ve inherited violated this principle. It’s all too easy to move view logic (which is better housed in a helper), or domain/model logic, into the controller.

The problem is that the controller object will start to violate the single responsibility principle making future changes to the code base difficult and error-prone. Generally, the only types of logic you should have in your controller are:

  • Session and cookie handling. This might also include authentication/authorization or any additional cookie processing you need to do.
  • Model selection. Logic for finding the right model object given the parameters passed in from the request. Ideally this should be a call to a single find method setting an instance variable to be used later to render the response.
  • Request parameter management. Gathering request parameters and calling an appropriate model method to persist them.
  • Rendering/redirecting. Rendering the result (html, xml, json, etc.) or redirecting, as appropriate.

While this still pushes the limits of the single responsibility principle, it’s sort of the bare minimum that the Rails framework requires us to have in the controller.

Common Mistake #2: Putting too much logic in the view

The out-of-the-box Rails templating engine, ERB, is a great way to build pages with variable content. However, if you’re not careful, you can soon end up with a large file that is a mix of HTML and Ruby code that can be difficult to manage and maintain. This is also an area that can lead to lots of repetition, leading to violations of DRY (don’t repeat yourself) principles.

This can manifest itself in a number of ways. One is overuse of conditional logic in views. As a simple example, consider a case where we have a current_user method available that returns the currently logged in user. Often, there will end up being conditional logic structures like this in view files:

  <% if current_user %>
    <%= current_user.name %>
  <% else %>
  <% end %>

A better way to handle something like this is to make sure the object returned by current_user is always set, whether someone is logged in or not, and that it answers the methods used in the view in a reasonable way (sometimes referred to as a null object). For instance, you might define the current_user helper in app/controllers/application_controller like this:

require 'ostruct'

helper_method :current_user

def current_user
  @current_user ||= User.find session[:user_id] if session[:user_id]
  if @current_user
    OpenStruct.new(name: 'Guest')

This would then enable you to replace the previous view code example with this one simple line of code:

<h3>Welcome, <%= current_user.name -%></h3>

A couple of additional recommended Rails best practices:

  • Use view layouts and partials appropriately to encapsulate things that are repeated on your pages.
  • Use presenters/decorators like the Draper gem to encapsulate view-building logic in a Ruby object. You can then add methods into this object to perform logical operations that you might otherwise have put into your view code.

Common Mistake #3: Putting too much logic in the model

Given the guidance to minimize the logic in views and controllers, the only place left in an MVC architecture to put all that logic would be in the models, right?

Well, not quite.

Many Rails developers actually make this mistake and end up sticking everything in their ActiveRecord model classes leading to mongo files that not only violate the single responsibility principle but are also a maintenance nightmare.

Functionality such as generating email notifications, interfacing to external services, converting to other data formats and the like don’t have much to do with the core responsibility of an ActiveRecord model which should be doing little more than finding and persisting data in a database.

So if the logic shouldn’t go in the views, and it shouldn’t go in the controllers, and it shouldn’t go in the models, well then, where should it go?

Enter plain old Ruby objects (POROs). With a comprehensive framework like Rails, newer developers are often reluctant to create their own classes outside of the framework. However, moving logic out of the model into POROs is often just what the doctor ordered to avoid overly complex models. With POROs, you can encapsulate things like email notifications or API interactions into their own classes rather than sticking them into an ActiveRecord model.

So with that in mind, generally speaking, the only logic that should remain in your model is:

  • ActiveRecord configuration (i.e., relations and validations)
  • Simple mutation methods to encapsulate updating a handful of attributes and saving them in the database
  • Access wrappers to hide internal model information (e.g., a full_name method that combines first_name and last_name fields in the database)
  • Sophisticated queries (i.e., that are more complex than a simple find); generally speaking, you should never use the where method, or any other query-building methods like it, outside of the model class itself

Common Mistake #4: Using generic helper classes as a dumping ground

This mistake is really sort of a corollary to mistake #3 above. As discussed, the Rails framework places an emphasis on the named components (i.e., model, view, and controller) of an MVC framework. There are fairly good definitions of the kinds of things that belong in the classes of each of these components, but sometimes we might need methods that don’t seem to fit into any of the three.

Rails generators conveniently build a helper directory and a new helper class to go with each new resource we create. It becomes all too tempting, though, to start stuffing any functionality that doesn’t formally fit into the model, view, or controller into these helper classes.

While Rails is certainly MVC-centric, nothing prevents you from creating your own types of classes and adding appropriate directories to hold the code for those classes. When you have additional functionality, think about which methods group together and find good names for the classes that hold those methods. Using a comprehensive framework like Rails is not an excuse to let good object oriented design best practices go by the wayside.

Common Mistake #5: Using too many gems

Ruby and Rails are supported by a rich ecosystem of gems that collectively provide just about any capability a developer can think of. This is great for building up a complex application quickly, but I’ve also seen many bloated applications where the number of gems in the application’s Gemfile is disproportionately large when compared with the functionality provided.

This causes several Rails problems. Excessive use of gems makes the size of a Rails process larger than it needs to be. This can slow down performance in production. In addition to user frustration, this can also result in the need for larger server memory configurations and increased operating costs. It also takes longer to start larger Rails applications, which makes development slower and makes automated tests take longer (and as a rule, slow tests simply don’t get run as often).

Bear in mind that each gem you bring into your application may in turn have dependencies on other gems, and those may in turn have dependencies on other gems, and so on. Adding other gems can thus have a compounding effect. For instance, adding the rails_admin gem will bring in 11 more gems in total, over a 10% increase from the base Rails installation.

As of this writing, a fresh Rails 4.1.0 install includes 43 gems in the Gemfile.lock file. This is obviously more than is included in Gemfile and represents all the gems that the handful of standard Rails gems bring in as dependencies.

Carefully consider whether the extra overhead is worthwhile as you add each gem. As an example, developers will often casually add the rails_admin gem because it essentially provides a nice web front-end to the model structure, but it really isn’t much more than a fancy database browsing tool. Even if your application requires admin users with additional privileges, you probably don’t want to give them raw database access and you would be better served by developing your own more streamlined administration function than by adding this gem.

Common Mistake #6: Ignoring your log files

While most Rails developers are aware of the default log files available during development and in production, they often don’t pay enough attention to the information in those files. While many applications rely on log monitoring tools like Honeybadger or New Relic in production, it is also important to keep an eye on your log files throughout the process of developing and testing your application.

As mentioned previously in this tutorial, the Rails framework does a lot of “magic” for you, especially in the models. Defining associations in your models makes it very easy to pull in relations and have everything available to your views. All the SQL needed to fill up your model objects is generated for you. That’s great. But how do you know that the SQL being generated is efficient?

One example you will often run in to is called the N+1 query problem. While the problem is well understood, the only real way to observe it happening is to review the SQL queries in your log files.

Say for instance you have the following query in a typical blog application where you will be displaying all of the comments for a select set of posts:

def comments_for_top_three_posts
  posts = Post.limit(3)
  posts.flat_map do |post|

When we look at the log file of a request that calls this method we’ll see something like the following, where a single query is made to get the three post objects then three more queries are made to get each of those objects’ comments:

Started GET "/posts/some_comments" for at 2014-05-20 20:05:13 -0700
Processing by PostsController#some_comments as HTML
  Post Load (0.4ms)  SELECT "posts".* FROM "posts" LIMIT 3
  Comment Load (5.6ms)  ELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 1]]
  Comment Load (0.4ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 2]]
  Comment Load (1.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 3]]
  Rendered posts/some_comments.html.erb within layouts/application (12.5ms)
Completed 200 OK in 581ms (Views: 225.8ms | ActiveRecord: 10.0ms)

ActiveRecord’s eager loading capability in Rails makes it possible to significantly reduce the number of queries by letting you specify in advance all the associations that are going to be loaded. This is done by calling the includes (or preload) method on the Arel (ActiveRecord::Relation) object being built. With includes, ActiveRecord ensures that all of the specified associations are loaded using the minimum possible number of queries; e.g.:

def comments_for_top_three_posts
  posts = Post.includes(:comments).limit(3)
  posts.flat_map do |post|

When the above revised code is executed, we see in the log file that all of the comments were collected in a single query instead of three:

Started GET "/posts/some_comments" for at 2014-05-20 20:05:18 -0700
Processing by PostsController#some_comments as HTML
  Post Load (0.5ms)  SELECT "posts".* FROM "posts" LIMIT 3
  Comment Load (4.4ms)  SELECT "comments".* FROM "comments" WHERE"comments "."post_id" IN (1, 2, 3)
  Rendered posts/some_comments.html.erb within layouts/application (12.2ms)
Completed 200 OK in 560ms (Views: 219.3ms | ActiveRecord: 5.0ms)

Much more efficient.

This solution to the N+1 problem is really only meant as an example of the kind of inefficiencies that can exist “under-the-hood” in your application if you’re not paying adequate attention. The takeaway here is that you should be checking your development and test log files during development to check for (and address!) inefficiencies in the code that builds your responses.

Reviewing log files is a great way to be tipped off to inefficiencies in your code and to correct them before your application goes into production. Otherwise, you may not be aware of a resulting Rails performance issue until your system goes live, since the dataset you work with in development and test is likely to be much smaller than in production. If you’re working on a new app, even your production dataset may start out small and your app will look like it’s running fine. However, as your production dataset grows, Rails problems like this will cause your application to run slower and slower.

If you find that your log files are clogged up with a bunch of information you don’t need here are some things you can do to clean them up (the techniques there work for development as well as production logs).

Common Mistake #7: Lack of automated tests

Ruby and Rails provide powerful automated test capabilities by default. Many Rails developers write very sophisticated tests using TDD and BDD styles and make use of even more powerful test frameworks with gems like rspec and cucumber.

Despite how easy it is to add automated testing to your Rails application, though, I have been very unpleasantly surprised by how many projects I’ve inherited or joined where there were literally no tests written (or at best, very few) by the prior development team. While there is plenty of debate about how comprehensive your testing should be, it is pretty clear that at least some automated testing should exist for every application.

As a general rule of thumb, there should be at least one high-level integration test written for each action in your controllers. At some point in the future, other Rails developers will most likely want to extend or modify the code, or upgrade a Ruby or Rails version, and this testing framework will provide them with a clear way of verifying that the basic functionality of the application is working. An added benefit of this approach is that it provides future developers with a clear delineation of the full collection of functionality provided by the application.

Common Mistake #8: Blocking on calls to external services

3rd party providers of Rails services usually make it very easy to integrate their services into your application via gems that wrap their APIs. But what happens if your external service has an outage or starts running very slowly?

To avoid blocking on these calls, rather than calling these services directly in your Rails application during the normal processing of a request, you should move them to some sort of background job queuing service where feasible. Some popular gems used in Rails applications for this purpose include:

In cases where it is impractical or infeasible to delegate processing to a background job queue, then you will need to make sure that your application has sufficient error handling and fail-over provisions for those inevitable situations when the external service goes down or is experiencing problems. You should also test your application without the external service (perhaps by removing the server your application is on from the network) to verify that it doesn’t result in any unanticipated consequences.

Common Mistake #9: Getting married to existing database migrations

Rails’ database migration mechanism allows you to create instructions to automatically add and remove database tables and rows. Since the files that contain these migrations are named in a sequential fashion, you can play them back from the beginning of time to bring an empty database to the same schema as production. This is therefore a great way to manage granular changes to your application’s database schema and avoid Rails problems.

While this certainly works well at the beginning of your project, as time goes on, the database creation process can take quite a while and sometimes migrations get misplaced, inserted out of order, or introduced from other Rails applications using the same database server.

Rails creates a representation of your current schema in a file called db/schema.rb (by default) which is usually updated when database migrations are run. The schema.rb file can even be generated when no migrations are present by running the rake db:schema:dump task. A common Rails mistake is to check a new migration into your source repo but not the correspondingly updated schema.rb file.

When migrations have gotten out of hand and take too long to run, or no longer create the database properly, developers should not be afraid to clear out the old migrations directory, dump a new schema, and continue on from there. Setting up a new development environment would then require a rake db:schema:load rather than the rake db:migrate that most developers rely on.

Some of these issues are discussed in the Rails Guide as well.

Common Mistake #10: Checking sensitive information into source code repositories

The Rails framework makes it easy to create secure applications impervious to many types of attacks. Some of this is accomplished by using a secret token to secure a session with a browser. Even though this token is now stored in config/secrets.yml, and that file reads the token from an environment variable for production servers, past versions of Rails included the token in config/initializers/secret_token.rb. This file often mistakenly gets checked into the source code repository with the rest of your application and, when this happens, anyone with access to the repository can now easily compromise all users of your application.

You should therefore make sure that your repository configuration file (e.g., .gitignore for git users) excludes the file with your token. Your production servers can then pick up their token from an environment variable or from a mechanism like the one that the dotenv gem provides.

Tutorial Wrap-up

Rails is a powerful framework that hides a lot of the ugly details necessary to build a robust web application. While this makes Rails web application development much faster, developers should pay attention to the potential design and coding errors, to make sure that their applications are easily extensible and maintainable as they grow.

Developers also need to be aware of issues that can make their applications slower, less reliable, and less secure. It’s important to study the framework and make sure that you fully understand the architectural, design, and coding tradeoffs you’re making throughout the development process, to help ensure a high quality and high performance application.

About the author

Brian VanLoo, United States
member since December 5, 2013
Brian is a freelance software developer with a long career in delivering technology value in the corporate world. Expertise with a variety of open-source technologies from those used in embedded systems to large-scale web applications. [click to continue...]
Hiring? Meet the Top 10 Freelance Ruby on Rails Developers for Hire in March 2019


One big mistake is of course going with Ruby/Rails from the start. Performance always matters, because it equals energy efficiency, economy, environment etc. etc.
Carlos Cervantes
For most companies shipping product is more important than just performance.
Tyler Olson
Carlos, totally agree. I was just about to say a very similar thing. Sure Sinatra or some other lighter-weight framework would most always be faster, but if it's not a personal project (that is, when you are getting paid to make something), development time is normally much more scarce than hosting resources. I mean, to put it simply, if we were focused solely on performance and resource allocation, we wouldn't be programming in Ruby at all. Ruby (and by extension, I think, Rails) is great for it's expression nature and ease to work with. That's why we use it.
Nice post! Thanks for sharing.
Dylan Conlin
Our Ruby on Rails server's carbon footprint was larger than a small city's. We switched to paper and never looked back. Not even after we lost our jobs.
Mock and stub to much, it's better to have slow tests that work and tell you the truth than liar tests that run fast.
Grzegorz Łuszczek
I have one question to mistake $6. Why you don't fetch only comments from database? Do you really need post in that case? https://gist.github.com/grzlus/86c3e1ea58bf4fb2e5a1 Of course if there is some conditions in this associations, write code like mine will be difficult.
If one can't see the problem and added costs of having to throw (and upgrade) 5 times the hardware at an application, and paying 5 times the electricity, to make the application perform nominally compared to a different development time, then there's no helping them.
Brian VanLoo
Yeah, I was actually trying to stay away from that debate as it currently rages on elsewhere. Also, I think if you're skilled and careful enough you can create fast tests with judicious use of mocks and stubs and better testable architectures that are high quality (i.e. aren't fragile, don't lie). Quality tests, appropriate use of mocks and stubs, and fast tests don't have to be mutually exclusive.
But it's easier to forget and to make tests that pass and don't work for example <pre> <code> # Controller class MyController < ApplicationController def create @model = MyModel if @model.create_data redirect_to some_path else render :new end end end #Test describe MyController do it "#create" do MyModel.any_instance.stub(create_data: true) post :create, data: {name: 'name'} expect(response).to redirect_to(some_path) end end </code> </pre> In this case if you change in MyModel the name of the method to create_registration this tests passes, I used to check if the methods exists before stubing, but it's way better to just create data. I use mock and stubs but a lot less.
I was thinking the exact same thing. Why not use `pluck` for the id while you're at it? Comments.where(post_id: Post.limit(3).pluck(:id))
Brian VanLoo
I agree, I likely wouldn't write a test like you put forth in your example (for one thing, any_instance is generally considered a pretty strong anti-pattern). In almost all cases I would at least make sure and have some form of integration test that would insure the coupling between the two objects remains correct such that changes to the create_data method name wouldn't go unnoticed. Those integration tests do run slower but I don't tend to run them as often during my development process. I haven't had a chance to play with it yet but I also think the RSpec 3 code has some kind of provision for insuring that some amount of your stubs/mocks are "correct" with respect to the objects they're imitating.
Brian VanLoo
I think those are valid options too. My point was, are you checking to make sure that the code you're writing is doing what you think it is, especially in the case of ActiveRecord queries? For instance, how do you know that your examples aren't making three queries instead of one (I'm pretty sure they'd only make the one but you can't really tell from inspecting the code itself)? The example I present is pretty much the stock "N + 1" queries problem and is even used in the official Rails Guides documents: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations. Actually, I'm not sure I'm a big fan of moving down to lower level query methods like "where" in these examples. You're no longer taking advantage of abstracting away how the comment association is being made (what if it is a HABTM? what if it doesn't use post_id as the foreign key?). While the AREL query building can be a leaky abstraction at times it seems like it's still better to use the higher levels of it as much as possible and only dive down into crafting some of your own SQL-building stuff when necessary. I will admit the example itself is a bit contrived but I have worked in code bases where the previous developers probably never looked at their log files. Seeing 100 pages of query logging all from a single request was pretty shocking. But if I used an example like that in this article I doubt anyone would ever really believe me that it happened ;)
Grzegorz Łuszczek
If you use pluck, you will perform 2 queries. One for fetch ids second to fetch Comments. If you use select and pass relation to method where AR detect it and use subquery like that: SELECT * FROM comments WHERE post_id IN(SELECT id FROM posts LIMIT 3)
Grzegorz Łuszczek
You're right, your example is a good way to solve N+1 queries problem. Many people who started working in rails don't understand what some methods really do and what happens. They get a layer of abstraction. Abstraction layer that, they can use only in trivial problems. As I answerd previous comment. This exapmple perform ony one query. (tested in rails 3 and 4 in old projects). This is verydummy solution, but when someone use another foreign key or add another conditions to association then he need to add it here also. For HABTM is also a simple solution. But if you want to use it you must understand exacly what you want do. Just copy paste some snippet from web isn't best practise. I belive you. one of my last projects has a performance problem. Including this prroblem also.
#5: Slower start up time should not make development slower if you have your Rails app set up properly (at least not much). This also goes for running tests. There are also many gems to help out with running tests or persisting the app’s state so you don’t have to re-load the entire app every single time. One of my favorites that I use all the time is ‘guard’. I’ll just leave that sucker running the entire time while I’m working on an app. Overall, it can definitely become a problem, but I think you’ll usually have bigger fish to fry performance/resource wise before you find the need to dig deep in to your Gemfile. Similar to what the author said, this is where ‘developer discretion’ comes in making sure you’re not just tossing in any old gem every time you need some functionality. I think the most important thing for a developer is to just be aware and understand these types of things. Really try to understand what’s happening deep down, and don’t just pass this stuff off as ‘magic’ like a lot of new Rails developers tend to do. The more you understand the underlying fundamentals, the faster you’ll learn and pick up other things as well. Very important imo. #7: Integration tests are great to have, but I usually find myself starting with unit tests on my models and other classes outside the controller. In a rails app, they tend to make up the ‘core’ or basis of most of your logic (just like points #1,2,3), and it just seems natural to me that you’d want to start here. You’re covering your bases for future controller or other logic to be written on top of them later on. Writing a bunch of integration tests early on can also be super frustrating when the overall functionality of the app can be changing rapidly. Integration tests are high level and tend to only cover specific flows through your application. For example, integration testing the flow of a user registering on the sign up page, instead of testing the underlying logic that’s not only used on that page, but other pages as well (ex: testing the user model that’s used on that page and the settings/login page). Not to mention unit tests are usually much less time consuming to write and they can help you quickly understand what’s going on deeper down in the system. Helps when it comes time to writing completely new features based on the same underlying models and classes. Another case of low hanging fruit here. One thing to keep in mind is that software is also part business/money/time related, and you need to take that into consideration as well. There’s a balance between how and when you’re testing, and actually succeeding in building the thing that you’re trying to build. #8: It definitely depends on the case, but if your app is built around an api that currently happens to be down, you’re probably going to want to do something more than just set the request to the side and keep going. In most cases the best thing to do is to set responsible request timeouts and show the user a nice error page that something is wrong (while also notifying the developers). On the other hand, if you’re just using an external service for a single piece to a larger system and you're not concerned if the request potentially doesn't succeed, you might want to just fail that part, show a more discrete error message, and keep going. I’m not sure when and why you’d ever want to start sending external requests into a background job or queue, unless it really is something that needs to be processed in the background. That just doesn’t sound right to me (did I miss something here?). You normally want the calls to be request blocking because the data you're looking for in the response is important right now, not later on. Also, caching or running requests in parallel can help in certain cases. Both of which I’d probably opt for before a delayed job. Easier to implement, and potentially less points of failure. If the api is that slow that you feel you need to do something like this on a large scale, it might be time to take a look at what you’re doing, find a new api, or tell them they are slow and crappy. :D Bottom line, an api request failing is often a big deal. Sometimes the best thing to do is to just be honest to the user and tell them something went wrong, instead of just cowboy-ing that shit and pretending nothing happened. :P
You are totally right. I retract my comment about `pluck`. I tried it in my project before posting, and here it seemed to do two queries, with `SELECT *` in both of them, which `pluck` would have taken care of. Just tried it again, and it seems to only do one query. Don't know what I tried yesterday then :)
My reason for using `pluck` was to get rid of the `SELECT *` part of the query. `SELECT *` can be just as bad as N+1 queries.
Brian VanLoo
Wow, some great points and very thorough! Regarding #5, you're definitely correct that you can mitigate test startup times with tools like spring and guard and to a certain extent startup times in development are less of a concern than the effects of bloated applications in production. I think it would be good to adopt a bit of frugality in gem usage from the start to set things on the right path down the road. Maybe it even takes care of some of the perceived performance problems others have brought up in comments here. #7: My personal preference is for outside-in testing. Something like what is described here: http://rubylearning.com/blog/2010/10/05/outside-in-development/. So if possible what I do is write a failing integration-level test and then start writing unit-level tests (also initially failing) that force me to write the code to make the integration test move on to its next failure. In the end I have a robust set of tests that test at both high and low levels that I've also been able to prove really are testing what I've intended. My suggestion for at least having integration tests stems from taking over codebases with no tests at all. At that point I wish more for an integration suite that hopefully covers the breadth of the application than perhaps a handful of unit tests that cover only a part of the application. #8: I'm very averse to application failures. Coupling to external systems worries me as a potential point of failure I may not have control over. To whatever extent possible I prefer not to make calls to external systems inside the boundaries of a request/response. If there is something I absolutely need to get from an external system I'd prefer to respond to the user right away and then have a bit of javascript to periodically check back with my application for the results (and perhaps throw a spinner up on the page while things are waiting). If the external system has become unresponsive at least your Rails application isn't hung waiting for the external system to respond (and the user's page isn't sitting there potentially blank). You can still provide a timeout and message to the user after waiting an appropriate amount of time. For requests that trigger an interaction with an external system where we don't need to provide information back to the user (like a welcome email) a queuing mechanism is definitely appropriate and likely very beneficial. If your external email system goes away for a while any additional emails that need to be sent will just queue up to eventually get sent when the email system comes back.
Grzegorz Łuszczek
Your idea is very good. Using pluck could save some time per request. But in this case Lazy AR Relations was better solution. This is happens because pluck returns array, and array must be fetched from DB. You probably found Heisenbug :)
Grzegorz Łuszczek
In my last project I found code, that fetch all records (~2k) from one table (with ~300 columns) and then paginate it with slice on array. Also there is no eager loading or counter cache, so amount of queries was terrifying.
Nice post. Sums up the points I made in a recent coaching session with a client.
Logs are not "the only real way" to observe N+1 query problems. You'll probably want to check out bullet: https://github.com/flyerhzm/bullet
I'm more of a dilettante than a developer, but it would seem to me that it would be handy to have a probabilistic mechanism whereby you configure a probability for the likelihood of calling a mock vs. a live remote service. With access to "good enough" randomness, if you made the probability of calling the actual service 0.1 and the mock 0.9, you gain the benefits of overall speedier test runs, but over a large number of test executions, have a high probability of complete test coverage using the live services. Please pardon my ignorance if this approach is far from novel.
Because I am aware of my ROR and programming effect on the environment. I stopped using the computer to program and have tried using the abacus instead. It is super hard at first but it does the job well after a while.
Augustin Riedinger
I think what you call a "mistake" covers different realities. Your first points are about best practices, and maintainability, and actually, each point is about "How to structure my MVC?" The following ones are about optimization. But optimization is something that comes later in the development process, sometimes not even necessary. The fact that Rails is always telling you how to do things doesn't make not-doing-it-this-way a mistake. Maybe something like Lesson learned makes more sense here.
The cost of 5 Amazon EC2 Large instances for 1 month: $504. If Rails can save 4 hours of developer productivity per month, it's worth the hardware performance hit.
Moving your logic out of you M's, V's, and C's can really help with that.
Ambesh kumar
Nice Post....!!!
what is the difference b/w pluck and select?
Here's an interesting Stackoverflow question that might help you: http://goo.gl/OfC542
thanks karthi...
Orban Botond
For the 2nd paragraph may I recommend the display_case gem? https://github.com/objects-on-rails/display-case
... not a single one of these was rails-specific. I came looking for *actual* common problems that are part of working with Rails / how Rails works around/with these issues. These could apply to ANY MVC Framework if not ANY development environment (not even just MVC webapps!) . The only relatively Rails-specific one was #9... wtf?
Great Post !!!!
Dariusz Pawlik
http://www.wtryskiwacz.com wtryskiwacze common rail i pompa - dużo ciekawych artykułów
Gianluca Tessarolo
I don't want to start Any blasphemous rumors But I think that RoR Got a sick sense of humor And when I migrate I expect to find him laughing You're right, it is a big mistake to use too much gems and plugins... I've tried to migrate a Rails 2.3 application to 4.2 with 50 models, 60 controllers, 150 views, it uses these plugins: acts_as_audited add_nested_fields auto_complete default_value_for exception_notification semantic-menu (customized) simple_time_select and the following gems: acts-as-taggable-on-2.0.6 formtastic-1.0.0.rc markcatley-responds-to-parent-1.0.20090923 pluginaweek-state_machine-0.7.6 sandrods-odf-report-0.1.2 attrtastic-0.2.2 has_scope-0.5.0 mislav-will_paginate-2.3.11 polyglot-0.3.1 searchlogic-2.4.21 authlogic-2.1.1 inherited_resources-1.0.6 mutter-0.5.3 responders-0.4.7 treetop-1.4.8 calendar_date_select-1.15 less-1.2.21 paperclip-2.3.3 rubyzip-0.9.1 attrtastic The migration process was a nightmare, I don't list all the updates that you must write, they are too much, but the real problem comes when some of the gems are not maintained anymore and you must search for an alternative solution and cannot find it... I think that when you write an application like an ERP you must use a framework and libraries that don't become incompatible at every major release... BTW: I stopped the migration and say to my customer that probably is a better solution to rewrite the system from scratch... Happy Easter to everyone...
Nguyen Tuan
nice post
Atul Shimpi
agree on all the best practises except #5. Gems enable code reuse and makes ur applications modular.
I'm a little bias because this has been a thorn in my side, but by embedding logic into the view you make it difficult to add people into your project 2/3 months into the future. Making it extremely difficult for new front end programmers to add new features without the server code. Maintainability shouldn't be the main goal, but it should be something to keep in mind while developing so you can develop code faster a couple of months down the line. Spending an extra hour or two to separate dependencies or follow a couple coding practices could actually help a project a month into the future.
Nice information. All this points are very informative. After reading this blog every developers will be alert by doing this common mistake while working on <a href="http://www.cryptextechnologies.com/">Rail development</a>.Thanks for sharing such a nice article.
Adding "bullet" would be a direct conflict with #5: Using too many gems But I guess if is defined in development group, there's no harm, at least in production
Orban Botond
Bullet unfortunately doesn't catch every N+1 query.
@boriscy:disqus : I think a smart combintation of both actually makes sense. Tests shouldn't take hours to execute. Regarding your code example, when mocking, I generally also assert than the correct method has been called : <pre><code> describe MyController do it 'calls MyModel.create_data' do expect_any_instance_of(MyModel).to receive(:create_data) post :create, data: {name: 'name'} end end </pre></code>
Great post man!
Jorge Andres Serrano Ardila
Great! Thank for sharing this!
Known Coward
Gems also commonly break when upgrading Rails. Try to migrate an app with lots of gems to Rails 5 right now.
Known Coward
Common Mistake #11: The tab this page is loaded in crashes when flash crashes. smh
Topher Hunt
I have a concern about #2 (Putting too much logic in the view): While I agree that messy view templates are a nightmare, I think the example solution you illustrate is worse than the problem it solves because it leaves us with "magic code" that appears to be identical to the `current_user` method we'd use in a controller, yet it mysteriously handles the nil case for you. 6 months from now, this will leave you digging into Devise's internals trying to figure out where that word "Guest" came from. In my experience, the biggest reason crufty apps are hard to maintain is not because there's to many LOC, but rather because it's hard to understand what certain lines are doing. When I extract view logic to helper methods, I take time to name those methods extremely carefully, sometimes even prepending the word `helper_` or some such. It makes for long ugly method names, but it leaves no ambiguity about where I should expect that method to be defined. I've found that painstaking attention to clear and consistent naming (even at the cost of brevity) has done more to keep my Rails code clean than object-orientation has done... probably because the structure of Rails undermines OOP from the get go.
#11 ignoring restful resources. i have also witnessed projects with messed up controller logic because of ignoring generated restful code. developer tend to serve their tasks or features instead of following these norms. they think they are saving their time. but in the long run this approach will break. developer should use callbacks before and after filters to make controller restful and thin. scaffold is a great tool every rails developer should get used to by now. it make sure that the generated code is restful. than you can use what ever you need ignoring the rest. This will not lead to junk or extra code as one might think while reading this comment. this extra code will eventually be used in order to fulfil the purpose of restfulness. because in the long run every resource will use all of its functions eventually.
Vladi Ivanov
How many are too many gems ?
Abdelkrim Tabet Aoul
N+1 Queries are easy to find and fix if you don't put too much logic on controllers
Megan Fox
I was diagnosed with Parkinson's disease nearly 4 years ago, at 51. I had a stooped posture, tremors, muscle stiffness, sleeplessness, slow movement. I was placed on Sinemet for 7 months and then Sifrol and Rotigotine was introduced which replaced the Sinemet but I had to stop due to side effects. Last year, I started on Parkinsons disease herbal treatment from Madida Herbal Clinic, this natural herbal treatment totally reversed my Parkinsons disease. Visit www.madidaherbalcenter.weebly.com or email [email protected] The treatment worked incredibly for my Parkinsons disease, i have a total decline in symptoms including tremors, stiffness, slow movement and others.
The way this reads implies that the main problem with this helper method is the name, but I think the more egregious thing is the database call. In my opinion, a helper method is one that has logic that would still be appropriate for the view if it were applied directly, and there is no situation where a database call is appropriate view logic. I actually question the value of the helper method at all in this example since the logic could have been handled as <%= current_user&.name || 'Guest' %>, and that would be a reasonably small amount of logic to put into the view. The main reason for a helper method at this point would be to replace 'Guest' with a constant in some yaml file or something. Even with that solution though, I would rather get the current_user from the controller. Even if you're using a library that exposes current_user to the view, it's much cleaner to put @current_user = current_user in your controller.
Venkata krishna M
Hi Guys, As this article is posted more than 4 years ago, Can I still follow the suggestions mentioned in the article? This article is really useful now?
comments powered by Disqus
Free email updates
Get the latest content first.
No spam. Just great articles & insights.
Free email updates
Get the latest content first.
Thank you for subscribing!
Check your inbox to confirm subscription. You'll start receiving posts after you confirm.
Trending articles
Relevant Technologies
About the author
Brian VanLoo
Ruby Developer
Brian is a freelance software developer with a long career in delivering technology value in the corporate world. Expertise with a variety of open-source technologies from those used in embedded systems to large-scale web applications.