Ruby Web Development Tips

A bunch of tips related to web development with ruby. Not all of them are ruby-specific though.

Productivity: Keyboard

Disable your caps-lock key and tweak your keyboard activation-delay and key-repeat-delay. I use really low values for the delays which makes editing so much faster. Nothing is more productivity-inhibiting than having to wait half a second before the cursor keys start repeating.

Permanent Redirects

Be careful with permanent redirects. Often it is impossible to correct them when they are already in place. For example if you accidentally do a permanent redirect from a sub-page of your site to your root url. First of all browsers will cache the permanent redirect and you have no control over that cache when the redirect is in place already. The only way to correct the redirect would be to do a permanent redirect from the target url back to the url that did the permanent redirect. But since you redirected to your root url, this is probably not what you want, because the root url should stay as it is. This is what you can do in order to get it right: Use a temporary redirect first and then test with curl if the redirects behave like intended. Then change them to a permanent redirect, but add a Cache-Control Header with max-age=86400, so you can correct the redirect in emergency situations. Only use a higher value after several days without problems.

Avoid Gems

Gems are like fast-food. You can add them quickly, but in the end your app will get bloated and it is very hard to get rid of gems that are already integrated in your app. Also, many gems dont have a great code quality. If you are good at programming, you’re often better off just implementing the functionality on your own. Not always of course, but probably more often than you might think. Also, many gems get you going quickly, but when you want to customize things they start to get in your way.

Migrations: Add null: false to every column

Too often the non-null constraints are forgotten. So add non-null constraints to every column by default. When you have done that you can think about which columns should allow NULL-values. In general: Make your constraints as strict as possible in the beginning. It is easy to relax them later on, but it is really hard to make weak constraints more strict later on. So start with strict ones.

Common Performance Problems

Some performance problems of ruby websites roughly come in this order when creating a site from scratch that grows over time:

  1. Missing DB indices.
  2. Creating many records during the request.
  3. Misconfigurations.
  4. N+1 queries.

What you can do is:

  1. Create appropriate indices. This means you have to inspect which queries are actually slow and then creating indices for those specific queries. Dont add loads of indices willy-nilly. Also, make sure that your queries actually use the indices.
  2. Create those records in the background by using e.g. sidekiq.
  3. Real life example: The default redis configuration saves the whole data to disk nearly periodically. If your redis database is hundreds of megabytes in size, this can freeze your SQL DB if it sits on the same server. Make sure you have appropriate monitoring tools set up.
  4. This is rather difficult. Of course, you can often reduce the number of slow requests by caching, but depending on the content of your site, thats often not possible. E.g. when the displayed content depends on the currently logged in user. You can use the bullet gem in order to detect N+1 queries, though IMO this is usually the easy part. The harder part is to eliminate them. Sometimes this is simply solved by an includes, but sometimes the queries are hidden in the view and rather difficult to eliminate.

Add a app/classes Folder for Classes that dont Fit Into Default Rails MVC

Rails only comes with a few default folders. This leads to developing the habit of only creating and thinking in objects that fit into the MVC categories. That ist bad. By adding a app/classes folder you now have a folder where all objects that do not fit into MVC can live. This gives you more freedom to do full OOP and leads you away from trying to fit everything into the MVC pattern.

Testing Pagination

Too often i have seen people implement a test for pagination like this:

def test_post_pagination
  posts = create_list(PostsController::POSTS_PER_PAGE+1, :post) # POSTS_PER_PAGE is 20
  visit posts_path
  # ...
end

Instead one should do:

def test_post_pagination
  stub_const("PostsController::POSTS_PER_PAGE", 1)
  posts = create_list(PostsController::POSTS_PER_PAGE+1, :post)
  visit posts_path
  # ...
end

So instead of creating 21 posts, you just create 2.

CSS/SCSS/SASS

  • Avoid ids. Ids have to be unique in your document. It happens too often that it turns out that an element that got an ID will be embedded multiple times in a document.
  • Every element should have maximally one (static)class, and that class should be semantic. Dont do .sidebar_element.right.boxed.special (haml/slim syntax).