One of the first things we learn in Rails which are greatly useful are ActiveRecord validations. However, since they are easy to add, it happens frequently that we are burdening our users with validations instead of making forms easier and clearer.
For instance, let’s suppose we are validating the Social Security Number (SSN) of an user on signup. A sample code would be:
class User 9
validates_numericality_of :ssn
validates_uniqueness_of :ssn
validates_as_ssn :ssn # Checks if a reserved or special SSN was sent
end
With the configuration above, if the user forgets to fill in the SSN, leaving it blank, four error messages related to the SSN field will be shown:
- SSN can’t be blank
- SSN is the wrong length (should be 9 characters)
- SSN is not a number
- SSN is invalid
The question is: if the user just left the field blank, why we should show all those errors to him? Are they all relevant?
Luckily, the solution is quite simple: we can make use of the :allow_blank option, so other validations won’t be triggered if the field is blank:
class User 11, :allow_blank => true
validates_numericality_of :ssn, :allow_blank => true
validates_uniqueness_of :ssn, :allow_blank => true
validates_as_ssn :ssn, :allow_blank => true
end
This is also a nice use case for the Object#with_options method added by Rails:
class User true do |v|
v.validates_length_of :ssn, :is => 11
v.validates_numericality_of :ssn
v.validates_uniqueness_of :ssn
v.validates_as_ssn :ssn
end
end
SSN is just an example, but we are frequently burdening our users in username, e-mail, password and many other fields.
Do not validate presence of confirmation fields
Another interesting subject about validations are the confirmation fields. We have the following note in the documentation of validates_confirmation_of:
validates_confirmation_of :password
# NOTE: This check is performed only if password_confirmation is not nil
And this is a feature. This means that you don’t need to give the :password_confirmation key when creating objects in console or when writing tests:
it "should be valid with valid attributes" do
User.new(:password => "123456").should be_valid
end
If the test fails, you are validating the presence of :password_confirmation, which is unnecessary. Since the :password_confirmation field will be available in the views, it will always be sent. So if the user leave it blank, it’s sent as blank value to the model and therefore will be checked. Just in the place it needs to be.
I’m experimenting with dropping the password confirmation altogether. Will the sky fall if we don’t confirm passwords?
Tim, is always good to confirm the password. What the post proposes is to not require the presence of the confirmation field. By default, the confirmation does not allow blank values, but it’s ok for it to be nil, which is the scenario that happens in your tests and console.
When you require the presence, you do not allow it to be nil anymore, which only adds unnecessary lines to your tests and make your console experience boring. 🙂
“Tim, is always good to confirm the password.”
Are you sure? Facebook doesn’t do it. Some people are also suggesting not to use masked password fields anymore as well. http://www.useit.com/alertbox/passwords.html
I agree with your post and I say let’s take it further and get away from doing things we’ve always done just because we’ve always done them.
I’m with Tim. Don’t burden users with password confirmations. For users who need it, there’s a nice jQuery plugin that allows users to show the password: http://www.unwrongest.com/projects/show-password/
Users who mistype can use the “forgot my password” link.
@Tim, sorry, at first I misunderstood you. 🙂 I never thought about that subject (not requiring password confirmation). Has someone already done some testing to see how many times people actually mistake the password?
I proposed a long time ago on the Ruby on Rails lighthouse account, that validations be able to be nested within blocks, so if one passes, the next is called. e.g.
validates_presence_of :email do
validates_format_of :email, :with => /^[^@\s]+@[^@\s]+$/ do
validates_uniqueness_of :email
end
end
Nice and clean, and it would stop at the first error it encountered.
The answer to the ticket was ‘no’ at the time, but I honestly think it should be reconsidered for Rails 3.
Sometimes it’s harder to set up the validations so they’re mutually exclusive. I wouldn’t exactly claim it’s a good idea, but more than once, as a quick and dirty approach to this, I’ve just implemented an error message renderer that refuses to show more than one message per validated field. It just chooses one at random. Take that first example:
* SSN can’t be blank
* SSN is the wrong length (should be 9 characters)
* SSN is not a number
* SSN is invalid
You don’t need a sophisticated heuristic to choose the right message, because if the SSN is blank, any one of those messages is enough to prompt a user to, you know, enter an SSN.
That´s a great note. Something to keep in mind, principally for Railers. It´s about avoidind unecessarilly stuffs. Fun, bcz I was reading about validations and callbacks yesterday at Rails Guides.
What about making a comma-separated error messages ?
Something like: * SSN can’t be blank, is the wrong length, is not a number and is invalid ?
I agree it is better user experience to see most relevant error message (e.g. SSN can’t be blank). And I think it is better to do both server side and client side validation. Client side validation gives faster response and is preferred. Server side validation is still required to prevent attack. If client side validation can be generated from model, that will be great.
@Guoliang definitely! Right now I’m working on a patch that allows you to extract the validations from the model. So it should not be hard to generate client validation from it!
@José
Note that was done before: http://agilewebdevelopment.com/plugins/client_side_validation
(but I’m not sure this works with latest Rails, etc)
@dubek yes, I know other implementations as well. but they all have to hack rails to retrieve validations reflections. we are trying to have an official api for that 🙂
Looking forward to it 🙂
@Othmane The problem with that is, now you still have three redundant error messages, and in addition your user has to parse a complex sentence.
Less is more with validations.
Validations put in without a real business reason are a waste of developers time, add significant testing requirements, and are really just scratching a private itch for the developer that put them in.
If you can’t justify them, or you can justify a contra position, don’t put them in. Use sparingly. End of.
José the post is excellent, I’ll admit I forgot the allow blank option and have written custom code to do the same thing! I don’t like the rails validations but still haven’t found the best way to do it. The comments here are helping. In related news: http://www.alistapart.com/articles/inline-validation-in-web-forms/
Err sorry Hugo the post excellent!
Thanks Tim! I hope we can always see your comments here.
See ya! =)
The validatable gem allows validations to before performed sequentially so that multiple error messages aren’t shown at once. Perhaps not so useful for rails yet, but it’s a nice solution to the problem your post discusses.
http://validatable.rubyforge.org/
Harry: If you don’t require the user to confirm their password, do you require that they confirm their email address? One of the two probably needs to be confirmed. If they mis-type their email address, the “forgot password” feature will be useless.
Tim: I definitely do not like the idea of using an unmasked password. Too much of a security risk. Imagine if you were using an ATM and your PIN number was visible to all the people standing in line behind you. Maybe your Facebook account isn’t as valuable, but it would certainly seem to indicate a laxness of security standards that your future clients may not be comfortable with.
Rhett: On the applications I work on there is a strong concern about losing users due to a complicated sign up so we do everything we can to minimize required fields and we try to delay sign up until later in the process. Adding more fields to the signup for a person who mistypes both their email address and their password isn’t important to us.
Security is subjective, and you’ll need to apply judgement as to whether usability or security is a bigger concern for the application that you are working on.
“Yes, users are sometimes truly at risk of having bystanders spy on their passwords, such as when they’re using an Internet cafe. It’s therefore worth offering them a checkbox to have their passwords masked; for high-risk applications, such as bank accounts, you might even check this box by default. In cases where there’s a tension between security and usability, sometimes security should win.” — Jakob Nielson
Tim: I definitely agree that sign up forms should be short. I often copy and paste the confirmation fields myself.
To build upon your Jakob Nielson quote, I think masking by default would be the way to go in this scenario.
If the password is not masked by default then I am assuming you would be using a textfield input with a type value of “text”. Presumably, this would allow the password to be cached in the user’s autocomplete field in plain readable text. The next user might be able to arrow down on the autocomplete field and view the password.
On the other hand, if the password is masked by default, then you are probably using an input with a type value of “password” and then using JavaScript to turn it to an input type of “text” after the checkbox is clicked.
Is that what you’re thinking? I’m not necessarily opposed to the idea, but I think that masked passwords are a convention that users are comfortable with. It seems like superfluous work to me and if you add a bunch of checkboxes, like “unmask password” and “remember me next time,” your login form begins to take up more real estate. That wouldn’t matter in a dedicated login page, but if you want your login form to be in the header of the website, the design might get a little cramped. Just my 2 cents.