Database Constraints Using Rails

Image borrowed from this post

If you have ever built applications using Ruby on Rails you are most likely familiar with ActiveRecord Validations that ship with the framework.

If you haven’t, they are basically restrictions set up in your models to ensure that only quality data is persisted to your database.

Active Record provides helper methods for some of the most common restrictions you might want to set up for your data, like: presence, uniqueness, length…And even allows you to write custom validations for those special cases.

Validations run right before saving an object to the database. If the object passes all validations then an SQL INSERT is executed persisting the new object in the database.

However, there are some exceptions where bad data can bypass validations and be persisted to the database. Which is why it is important to set up that extra protection layer in your database using constraints.

Constraints are similar to validations in the sense that they serve the same purpose, protect the database from bad data but there is no bypassing these guys. Improving the accuracy and reliability of your database even if bad data bypasses validations

Constraints can be applied to column(s), tables(s), and/or even the entire database schema! Using rails we can set some of the most common constraints, NOT NULL UNIQUE DEFAULT CHECK , in our migration files.

Like I mentioned earlier, validations do a great job at ensuring only quality data is persisted. However, there are some cases, like a race condition, where they can be bypassed or even skipped with certain methods.

Consider a situation where two users are trying to claim the same username and submit the form at the same time. Introducing all kinds of bugs to your application.

This is a common case where validations are bypassed. Now, if there would have been a constraint in place to check for username uniqueness some exceptions would have been raised which you can then rescue and maybe respond with a status 500.

If your database is the only source of truth in your application you should strongly consider adding some constraints to it.

You should not have to write constraints for every single column in your database. A good rule of thumb is to write validations for data that the user can manipulate/change as they return some friendly errors when they fail.

If you set some constraints early and later find the need to insert a value that does not pass the constraint you would have to change your schema and it can take a while depending on the size of your database. Because of this is, it is recommended to set constraints that you know will ALWAYS apply. Like a presence check for email.

There are other occasions when you should add that extra protection like:

  1. We already ruled out the duplicate username example above, so always set uniqueness constraints for data that you don’t want duplicated and presence for columns that are required for your app to run accordingly.
  2. Constraints can be used to improve database performance when you add indexes for uniqueness constraints. Consider adding some indexes if your queries start to slow down.
  3. If you ever need to add some logic to your database you can use a check constraint. For example, checking a phone number meets a certain length.

There are pros and cons to writing too many constraints. Just like there is pros and cons for under constraining your database. It varies depending on your need so make sure to consider all options.

Validations are an amazing an easy way to protect your database. However, it is not always successful at this. Which is why we should always use some kind of constraints in our databases.

Databases are the most important part of an application. Which is why we should take all steps to protect it and ensure only quality data is persisted. At the end of the day, what is an application without data?

Junior Developer trying to help other developers(and myself) better understand programming.