Cover image
Web Front-end
12 minute read

Buggy Code: 10 Common Rails Programming Mistakes

Rails is both easy to use—and also to misuse. Let’s look at 10 common Rails programming mistakes, explore their consequences, and discover ways to steer clear, as we write clean Ruby on Rails code.

Editor’s note: This article was updated on 11/30/2022 by our editorial team. It has been modified to include recent sources and to align with our current editorial standards.

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 and promises a favorable outcome to expert developers who follow best practice conventions.

While this paradigm has its advantages, it is not without its pitfalls and leaves the door open for developers to make common Rails programming mistakes. The magic that happens behind the scenes in the framework can sometimes lead to developer confusion. It can also have undesirable ramifications with regard to security and performance.

Accordingly, Rails is easy to use—and equally easy to misuse. This tutorial looks at 10 common Ruby on Rails coding problems and shows you how to avoid them and the issues that they cause.

Common Rails Programming Mistake #1: Overloading the Controller With Logic

Rails is based on an MVC architecture. In the Rails community, we’ve been talking about fat model, skinny controller for a while, yet several recent Rails applications I’ve inherited violate 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 with this is that the controller object will start to violate the single responsibility principle, making future changes to the codebase difficult and prone to errors.

Generally, the types of logic you should have in your controller are:

  • Session and cookie handling. This might include authentication/authorization or other cookie processing.
  • Selecting a model. The logic for finding the right model object, given the parameters passed in from the request. Ideally, model selection should be a call to a single find method that sets an instance variable to be used later to render the response.
  • Requesting 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 pushes the limits of the single responsibility principle, it’s the bare minimum that the Rails framework requires us to have in the controller.

Common Rails Programming Mistake #2: Overloading the View With Logic

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 can also lead to repetition and violation of the Don’t Repeat Yourself (DRY) principle.

This unwieldy mix of HTML and Ruby can manifest itself in a number of ways, one of which is the overuse of conditional logic in views. As a simple example, consider a case in which we have a current_user method available that returns the currently logged-in user. Often, in such a case, we would wind up with conditional logic structures such as this in view files:

<h3>
    Welcome,
    <% if current_user %>
        <%= current_user.name %>
    <% else %>
        Guest
    <% end %>
</h3>

In lieu of embedding conditional logic in our HTML, it would be better to make sure the object returned by current_user is always set—whether or not someone is logged in (i.e., null if someone is not logged in)—and that view methods can handle a null user reference. 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
        @current_user
    else
OpenStruct.new(name: 'Guest')
    end
end

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>

Here are a couple of additional recommended Ruby on Rails coding best practices:

  • Use view layouts and partials appropriately to encapsulate repetitive items on your pages.
  • Use presenters/decorators like the Draper gem to encapsulate view-building logic in a Ruby object. You can then add methods that perform logical operations into this object—instead of putting these into your view code.

Common Rails Programming Mistake #3: Overloading the Model With Logic

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 store everything in their ActiveRecord model classes, which leads 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—doesn’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, where should it go?

Enter Plain Old Ruby Objects (POROs). With a comprehensive framework like Rails, newer developers might be 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 housing 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 and saving a handful of attributes 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 that are more complex than a simple find; generally speaking, you should never use the where method, or any other query-building method like it, outside of the model class.

Common Rails Programming Mistake #4: Using Generic Helper Classes as Dumping Grounds

This mistake is a corollary to the preceding mistake number 3. The Rails framework places an emphasis on the named components of an MVC (i.e., model, view, and controller) framework. While definitions exist to describe what we can place in the classes of each of these components, there are times when the methods we develop do not seem to fit into any of the three.

A Rails generator conveniently builds a helper directory and a new helper class to go with each new resource we create. It can be all too tempting to stuff any functionality that doesn’t fit into the model, view, or controller into a helper class.

While Rails is certainly MVC-centric, there is nothing to prevent you from creating your own types of classes and adding appropriate directories to contain the code for those classes. When you develop new functionality, think about which methods group together well and assign descriptive names to 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 Rails Programming Mistake #5: Too Many Gems

Both Ruby and Rails are supported by a rich ecosystem of gems that collectively provide just about any capability a developer can think of—great for building up a complex application quickly. However, I’ve also seen many bloated applications whose Gemfile is disproportionately large compared with the functionality it offers.

Excessive use of gems makes the size of a Rails process larger than it needs to be, slowing 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.

An overabundance of gems can cause a heftier Rails application to take longer to start, 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 an application may, in turn, have dependencies on other gems, and those may also have their own dependencies. Adding gems can thus have a compounding effect. For instance, adding the rails_admin gem will bring in 11 more gems in total—a 10% increase from the base Rails installation.

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

Carefully consider whether the extra overhead is worthwhile as you add each gem. As an example, developers will often add the rails_admin gem that provides a nice web front end to the model structure. But this gem 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. You’d be better served by developing your own, more streamlined administration function.

Common Rails Programming Mistake #6: Ignoring Log Files

While most Rails developers may be aware of default log files, a woefully small proportion actually heed the information in those files. In production, tools such as Honeybadger or New Relic can handle log monitoring for us. However, it is crucial to keep an eye on log files as we develop and test our applications.

Much of Rails’ magic happens in the application’s models. By defining associations in your models, it is 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 confirm that the SQL being generated is efficient?

One example you will often run into is the N+1 query problem. While impossible to observe in real time, the N+1 query problem is well documented and detectable by reviewing our logged SQL queries.

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

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

The log file of a request that calls this method might reveal that a single query gets three post objects, and is followed by three more queries to get each object’s comments:

Started GET "/posts/some_comments" for 127.0.0.1 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 when 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|
        post.comments.to_a
    end
end

If we check the log file after executing our revised code, we will see that comments for all of the post objects were collected in a single query:

Started GET "/posts/some_comments" for 127.0.0.1 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 query problem is an example of the kind of inefficiencies that we might find if we peered under the hood of an application in which the developer hadn’t been attentive to the log files.

Reviewing development and test log files during development is a great way to be tipped off to any inefficiencies in your code so that you can correct your work before the application goes into production. And since the dataset you work with in development or testing is likely to be much smaller than one that’s in production, address it as early in the development process as possible. If you’re working on a new app, your production dataset may start out small but, as it grows, Rails will slow down your application.

Consider taking steps to clean your log files of superfluous or inessential information.

Common Rails Programming Mistake #7: Lack of Automated Tests

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

Despite the ease with which we can add automated testing to a Rails application, I have been unpleasantly surprised by the number of projects I inherited or joined where there were literally no tests written—or, at best, very few. 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. If, at some point in the future, a developer extends or modifies the code, or upgrades a Ruby or Rails version, 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 a clear delineation of the full type of functionality provided by the application.

Common Rails Programming Mistake #8: Blocking on Calls to External Services

Third-party providers of Rails services tend to 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 such calls, rather than calling these services directly in your Rails application during the normal processing of a request, where feasible, move them to some sort of background job-queuing service. 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, 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 disconnecting your app server from the network) to verify that it doesn’t result in any unanticipated consequences.

Common Rails Programming 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 a great way to manage granular changes to your application’s database schema and avoid Rails problems.

While it certainly works well at the beginning of your project, as time goes on, the database creation process can start to take longer to complete, and a migration could be misplaced, inserted out of order, or introduced from other Rails applications using the same database server.

By default, Rails creates a representation of your current schema in a file called db/schema.rb. This file is usually updated when database migrations 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, without its 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, you should not be afraid to clear out the old migrations directory, dump a new schema, and continue 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. Additional information on this topic is located in the Rails Guide.

Common Rails Programming Mistake #10: Checking Sensitive Information Into a Source Code Repository

The Rails framework makes it easy to create secure applications that remain impervious to many types of attacks. Some of this is accomplished by using a secret token to secure a session with a browser. Such a token is stored in config/secrets.yml, and our app reads the token from an environment variable for production servers.

Previous versions of Rails included the token in config/initializers/secret_token.rb, which is often mistakenly checked into the source code repository with the rest of your application, and, anyone with access to the repository could easily compromise all users of your application.

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 we’ve identified. This will make your applications more easily extensible and maintainable.

Beware of issues that can make your applications slower, less reliable, or less secure. It’s important to study the framework and make sure that you fully understand the architectural, design, and coding trade-offs you’re making throughout the development process to help ensure a high-quality and high-performance application.

Further Reading on the Toptal Engineering Blog:

Understanding the basics

Model, view, controller (MVC) came about as an architecture focused on separation of concerns. The model holds the data for the main application objects. The view should contain only that logic to transform data into a user interface. Controllers respond to the main system events, often tied to application routes, that eventually lead to updated models and views.

Ruby on Rails helpers are functions usually written to share code blocks throughout an application. There are both built-in and custom helpers, and these helpers are used just like functions within an application’s codebase.

RubyGems is a package manager for Ruby. Packages contain code used to modify or extend functionality within an application, and may include documentation and utilities.

Rails makes testing simple with the flexibility to run either all tests or a specific one. The bin/rails test command is the method for initiating these tests. If a specific test is not mentioned, then all tests for an application will run.

A Rails log file contains the information output by an application while running. The logged information will be limited to the log output level, ranging from high-level errors to verbose debugging information.

A Rails service is an object that allows developers to separate the application’s business logic from the controllers and models. This separation typically allows for simpler controller and model logic.

Rails migration allows encapsulated database schema changes to be defined and potentially stored in a version control system. Once shared with other developers in a team, these migrations may be run to update data schemas with a command execution.

Ruby on Rails has an out-of-the-box default security framework that provides basic protection from common attack vectors.

Comments

whycantibeanon
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.
whycantibeanon
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.
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.
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.
matheusml
Nice post! Thanks for sharing.
matheusml
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.
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.
boriscy
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.
boriscy
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.
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.
whycantibeanon
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.
whycantibeanon
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.
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.
boriscy
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#create_data to MyModel#create_registration this tests passes, I check if the methods exists before stubing but it's way better to just create data and see if it actually saves. I use mock and stubs but a lot less.
boriscy
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#create_data to MyModel#create_registration this tests passes, I check if the methods exists before stubing but it's way better to just create data and see if it actually saves. I use mock and stubs but a lot less.
Mads Ohm Larsen
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))
Mads Ohm Larsen
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 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 ;)
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
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.
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.
Nick
#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
Nick
#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
Mads Ohm Larsen
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 :)
Mads Ohm Larsen
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 :)
Mads Ohm Larsen
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.
Mads Ohm Larsen
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.
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
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.
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.
macarthy
Nice post. Sums up the points I made in a recent coaching session with a client. One point you should also add : Put interfaces between you code and 3rd party services and/or gems . If the disappear then you can swap out a similar service/ or code. Also you can mock the interface for fast tests.
macarthy
Nice post. Sums up the points I made in a recent coaching session with a client. One point you should also add : Put interfaces between you code and 3rd party services and/or gems . If the disappear then you can swap out a similar service/ or code. Also you can mock the interface for fast tests.
Simone
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
Simone
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
Anthony Ortenzi
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.
Anthony Ortenzi
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.
Gus
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.
Gus
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.
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.
Guest
The cost of 5 Amazon EC2 large instances for 1 month: $504. The cost of 1 developer hour: $100. If Rails can save 4 hours of developer productivity per month, it's worth the hardware performance hit.
Guest
The cost of 5 Amazon EC2 large instances for 1 month: $504. The cost of 1 developer hour: $100. If Rails can save 4 hours of developer productivity per month, it's worth the hardware performance hit.
Guest
Moving your logic out of you M's, V's, and C's can really help with that.
Guest
Moving your logic out of you M's, V's, and C's can really help with that.
Ambesh kumar
Nice Post....!!!
Ambesh kumar
Nice Post....!!!
iyyappan
what is the difference b/w pluck and select?
iyyappan
what is the difference b/w pluck and select?
Karthik K
Here's an interesting Stackoverflow question that might help you: http://goo.gl/OfC542
Karthik K
Here's an interesting Stackoverflow question that might help you: http://goo.gl/OfC542
iyyappan
thanks karthi...
iyyappan
thanks karthi...
Orban Botond
For the 2nd paragraph may I recommend the display_case gem? https://github.com/objects-on-rails/display-case
Orban Botond
For the 2nd paragraph may I recommend the display_case gem? https://github.com/objects-on-rails/display-case
wtf?
... 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?
wtf?
... 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?
h4hardikonly
Great Post !!!!
h4hardikonly
Great Post !!!!
Dariusz Pawlik
http://www.wtryskiwacz.com wtryskiwacze common rail i pompa - dużo ciekawych artykułów
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...
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
Nguyen Tuan
nice post
Atul Shimpi
agree on all the best practises except #5. Gems enable code reuse and makes ur applications modular.
Atul Shimpi
agree on all the best practises except #5. Gems enable code reuse and makes ur applications modular.
Steve
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.
Steve
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.
Angelaaustin
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.
Angelaaustin
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.
kakukiko
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
kakukiko
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.
Orban Botond
Bullet unfortunately doesn't catch every N+1 query.
Loic WAMLAND
@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> before(:each) do allow_any_instance_of(MyModel).to receive(:create_data).and_return(true) end 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>
Loic WAMLAND
@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> before(:each) do allow_any_instance_of(MyModel).to receive(:create_data).and_return(true) end 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>
Arturo
Great post man!
Arturo
Great post man!
Jorge Serrano
Great! Thank for sharing this!
Jorge Serrano
Great! Thank for sharing this!
Known Coward
Common Mistake #11: The tab this page is loaded in crashes when flash crashes. smh
Known Coward
Common Mistake #11: The tab this page is loaded in crashes when flash crashes. smh
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
Gems also commonly break when upgrading Rails. Try to migrate an app with lots of gems to Rails 5 right now.
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.
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.
bilalbash
#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.
bilalbash
#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 ?
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
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 madidaherbalcenter@gmail.com. The treatment worked incredibly for my Parkinsons disease, i have a total decline in symptoms including tremors, stiffness, slow movement and others.
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 madidaherbalcenter@gmail.com. The treatment worked incredibly for my Parkinsons disease, i have a total decline in symptoms including tremors, stiffness, slow movement and others.
Watwatwat
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. To put this another way: All of the view's knowledge should come from the controller. Helper methods should not be a source of knowledge but a way for the view to process knowledge it has received from its controller. It helps to think of the connection between a controller and its view as if the controller were an API delivering JSON to your view front end. Helper methods shouldn't do anything that you couldn't do with JavaScript instead. They're just easier than trying to include JavaScript in your app if you haven't really set your app up to use JavaScript a lot.
Watwatwat
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. To put this another way: All of the view's knowledge should come from the controller. Helper methods should not be a source of knowledge but a way for the view to process knowledge it has received from its controller. It helps to think of the connection between a controller and its view as if the controller were an API delivering JSON to your view front end. Helper methods shouldn't do anything that you couldn't do with JavaScript instead. They're just easier than trying to include JavaScript in your app if you haven't really set your app up to use JavaScript a lot.
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?
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?
Rose Williams
Re #3: What about predicates methods in the model ?
Rose Williams
Re #3: What about predicates methods in the model ?
Sam Watt
Thanks Brian, for sharing this nice and informative post, I agree with you what most of the <a href="https://www.botreetechnologies.com/ruby-on-rails">Ruby on Rails Developers</a> ignore their log files which sometimes creates a big problem
Gary Andrews
I will try to avoid these mistakes. Your post will help me with that. Thanks!
Paul Verschoor
Regarding point #9 (Getting married to existing database migrations): I agree, but when your migration get out of hand, shouldn't you then remove all migrations AND make a copy of `db/schema.rb` and call it `db/migrate/20210101080000_snapshot_from_schema` This would keep your developing command the same (e.g. `rails db:migrate`). After all, we are creatures of habit.
comments powered by Disqus