Archive for November 2009

 
 

Validates uniqueness of multiple columns

We came across a case where we wanted to validate that the combination of first and last names for a new person was unique. Add the following snippet

#config/initializers/validators.rb
ActiveRecord::Base.class_eval do
  def self.validates_uniqueness_of_combined(*attr_names)
    options = attr_names.extract_options!.symbolize_keys
    attr_names = attr_names.flatten

    send(validation_method(options[:on] || :save), options) do |record|
      sql             = attr_names.map{ |attr_name| "UPPER(#{attr_name}) = ?"}.join(" AND ")
      values       = attr_names.map{ |a| record.send(a) }.map{ |v| v && v.upcase }
      conditions = [sql, *values]

      db_record = record.class.find(:first, :conditions => conditions)
      if db_record && db_record != record
        default_message = "#{attr_names.map{ |attr_name| record.send attr_name }.join(' ')} has already been added"
        record.errors.add_to_base(options["message"] || default_message)
      end
    end
  end
end

to your initializers then you can do

class Person < ActiveRecord::Base
  validates_uniqueness_of_combined :first_name, :last_name
end

Note that this does a case insensitive match on the column names. If you want case sensitive you should replace

      sql             = attr_names.map{ |attr_name| "UPPER(#{attr_name}) = ?"}.join(" AND ")
      values        = attr_names.map{ |a| record.send(a) }.map{ |v| v && v.upcase }

with

      sql             = attr_names.map{ |attr_name| "#{attr_name} = ?"}.join(" AND ")
      values        = attr_names.map{ |a| record.send(a) }

Put your team back together with some promiscuous pairing

It’s happened so many times. The big release is coming up, we realise there isn’t enough time to get all the necessary features in, the business demands that we drop quality to hit the deadline. We reluctantly comply and… we hit the deadline. The business and more importantly our customers are delighted. Some big ticket customers that were going to cancel contracts with us don’t. Kinda difficult to argue with that.

Problems

Of course there was a cost and now we, the development team were feeling it, bad. The project is full of broken windows, there’s buildings on fire and crack dealers on the corner.

  • inconsistent and poor quality
  • conflicting views
  • ownership - people were protective of code
  • unhappy developers
  • poor team mentality
  • low velocity

The Solution

One night I come across a paper that I’ve read once before, promiscous pairing and beginners mind. While I found it interesting the first time I didn’t persue it. As I reread I started to wonder if perhaps it might provide us with a way to put the team back together and to bring the project back to a state of pride. So how’s it work?

Promiscuous pairing - pair up, after an hour and a half, the developers in the driving seat moves on to the next story. Back seat developers move into the driving seat and are joined by a new developer, etc.

Result

The effect was noticeable the first day. After a week we had a team with

  • Higher energy
  • Higher velocity
  • Higher quality
  • Happy developers
  • More fun

Why

  • Every time a switch takes place the developer staying on a story must explain the story to the new developer. This is what creates the “beginners mind”, it ensures that focus is always on the requirements of the story.
  • Juniors received continuous guidance allowing them to produce same quality of code as seniors.
  • Don’t get stuck in rabbit holes, those oh so clever little ideas that turn into sprawling epics get nipped in the bud
  • All design decisions are discussed and agreed upon leading to consensus across the team
  • Discussing all problems in english encourages ubiquitous language and semantic match with business.
  • Remain focused, never get stuck because other person always has ideas, get completely stuck? - every hour and a half you get a fresh pair of eyes on the problem.
  • Each team member has different strengths, rotation means all strengths are applied to all tasks
  • Knowledge spread - every key shortcut, refactoring trick, test technique, domain modelling principle shared between all team members.
  • Risk reduction - all members of team are up to speed with all technologies and features in project so don’t need to worry about holidays/illness/turnover.
  • Stress reduction - no one is responsible for delivering a feature, focus is on how best to move story/bug along in next hour and a half.
  • No resentment about features being implemented poorly
  • Team mentality - everyone did everything