Mark Daggett's Blog

Innovator & Bricoleur

The Minimally Viable Party

Garry and I are planning our next big project together. In the spirit of agile development and with the reality of limited funds we are ruthlessly scoping our efforts around a minimal feature set. We want to develop just enough of the product to see if we have a hit. Typically, this process is described as developing the minimum viable product (MVP).

The MVP approach targets the hardcore vocal minority that understand your offering, and are likely to give you helpful insights on how to improve it. With this in mind we began to list our potential features and aggressively cut anything that wasn’t essential.

We tried a variety of approaches to identify our MVP. Which included:

  • Sorting features in order of complexity, and identifying those with serial dependencies
  • Selecting only those features that touch the revenue line (a topic for another post)
  • Determining those features which could give us a competitive advantage over other similar products.

These thought experiments were helpful, but the focus felt very myopic, and more about cutting than pruning; like shaping a bonsai tree blindfolded. However, while mowing the lawn (where I do much of my good thinking), I came up with a new approach: “The Minimum Viable Party”.

A party seemed like a perfect metaphor for these reasons:

  • The goal of product at this stage, is to meet people, show them a good time and give them a complete experience they can give feedback on.
  • Parties are events with a specific beginnings and ends. Being the host narrows your responsibilities to just throwing a great party. If you find yourself needing to first build the venue, or starting a catering company at the same time then you are doing it wrong.
  • Parties are fun, (even Goth Emo parties); they are about doing something you love, with others looking for the same thing.
  • A complete party is more than just good food. There are many aspects that can be considered including venue, theme, duration, etc.
  • If it all goes horribly wrong you can recover. You just clean up the mess, pull the lawn chairs off the roof, get a tow company to dredge your car from the neighbors pool and go on with your life.
  • By breaking a party into smaller components you can map them onto the MVP. Now, I am not for a minute claiming that there is an absolute one-to-one mapping between party to product. However, the metaphor did allow me to consider the attributes of my product in a more objective and holistic way. For example, a decision on whether to spend money on party invitations could be construed as a marketing spend on promoting our product.

Planning a Minimal Viable Party

Here are the rules for the Minimal Viable Party thought experiment:

  • You are planning a party for people you do not know.
  • You have one week to plan and execute your party.
  • Without specifying a specific amount you should assume that funds are very limited, which should force you to make decisions on how and where to spend your money.
  • The party is not a catered meaning that much if not all the work should be done personally.

These rules lead me to a series of questions to consider which i’ve detailed below:

Q. How many guests should I invite?

A. You should invite the number of guests you can host comfortably. Everyone wants to feel special at the party, meaning you should know your limits before the inviting others.

Insight: Many people focus on the hockey stick style growth from the outset, that is a result of a good product not the goal itself. At this stage the goal is to get to know the users, and the only way to do that is to ensure there is enough of you to go around.

Q. How do I entertain people I have never met?

A. Plan a party around the type of guest you’d want to see again. If you are a geek at heart then have your party on the holodeck and don’t mind the haters.

Insight: You can’t please everyone but it’s important that you understand who you’d like as customers and friends. Ensure that your product gives them a memorable and enjoyable experience.

Q. What kind of food should I cook?

A. Be honest about your own cooking skills, anything you don’t do well you should either eliminate or buy (even if this means you have to buy all the food).

Insight: No one wants to eat bad food, a strangers will not give you an “A” for effort when eating your half-cooked hamburgers. The same is true for a poorly executed product. I continually have to fight the urge to be an everything expert. While striving to learn new things is a positive, not knowing (or ignoring) your weaknesses limits you from being effective under a deadline.

Q. How many courses should I prepare?

A. What would your ideal guest expect? Not everyone expets (or even wants) a five course meal that takes hours to eat. What they will want is for it to feel complete, and that differs from person to person.

Insight: The expression “soup to nuts” is often used when describing a project completed from beginning to end. It alludes to a complete meal that included appetizers (soup), nuts (dessert) and everything in between. If you view your features through the lens of completeness it should help you determine if a feature is needed now or can wait.

Enterprise Quality Login Validation for Rails 3

In the world of enterprise software development sometimes you need to be a little more thorough with your validations than you would for other types of clients. Here is a simple yet effective login validator that passes the typical forms of penetration testing you will find in the enterprise world.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class LoginFormatValidator < ActiveModel::EachValidator
  def validate_each(object, attribute, value)
    if value.present?
      unless value.gsub(" ",'') == value
        object.errors[attribute] << (options[:message] || "cannot contain any whitespace")
      end
      if [value[0], value[-1]].any?{ |x| x == "." }
        object.errors[attribute] << (options[:message] || "cannot contain a period at the start or end")
      end
      unless value =~ /[a-zA-Z]/
        object.errors[attribute] << (options[:message] || "must contain at least one letter")
      end
      unless value =~ /[0-9]/
        object.errors[attribute] << (options[:message] || "must contain at least one number")
      end
      unless value =~ /[_\-.]/
        object.errors[attribute] << (options[:message] || "must contain at least one these special characters \"-_.\"")
      end
      unless value =~ /^[a-zA-Z0-9_\-.]+$/
        object.errors[attribute] << (options[:message] || "can only contain letters number and special these special characters \"-_.\"")
      end
    end
  end
end

You can use it this way:

1
validates :login, :presence => true, :login_format => true, :uniqueness => { :case_sensitive => false }, :length => { :maximum => 64, :minimum => 6 }

Here are some specs to prove it works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
it "should only allow letters numbers and spaces in the name attribute" do
  ['foo', 'foo bar baz', 'foo 123 - bar baz'].each do |n|
    @user.name = n
    @user.save
    @user.errors[:name].should be_empty
  end
  @user.name = '<script>alert("test");</script>'
  @user.save
  @user.errors[:name].first.should == "is invalid"
end

it "should not allow you to change an email for an existing record" do
  @user.email = "test@test.com"
  @user.save
  @user.errors[:email].first.should == "cannot be changed once assigned"
end

it "should be case insenitive" do
  @user2 = Factory.build(:user)
  @user2.login = @user.login.capitalize
  @user2.save
  @user2.errors[:login].first.should == "has already been taken"
end

it "should be at least 6 characters and no more than 64 characters" do
  @user2 = Factory.build(:user, :login => 'a1_')
  @user2.save
  @user2.errors[:login].first.should == "is too short (minimum is 6 characters)"
  @user2.login = 'Pneumonoultramicroscopicsilicovolcanoconiosis1_is_a_very_long_word'
  @user2.save
  @user2.errors[:login].first.should == "is too long (maximum is 64 characters)"
end

it "should ensure the name is a mix of alpha chars (A-Z or a-z), numeric chars (0-9), and special characters (- _ .)" do
  @user2 = Factory.build(:user, :login => '1----.----1')
  @user2.save
  @user2.errors[:login].first.should == "must contain at least one letter"
  @user2.login = "a------------------a"
  @user2.save
  @user2.errors[:login].first.should == "must contain at least one number"
  @user2.login ="abc12345676789"
  @user2.save
  @user2.errors[:login].first.should == "must contain at least one these special characters \"-_.\""
end

it "should ensure the special character '.' is not used at the beginning and end" do
  @user = Factory.build(:user, :login => '.Admin_f00')
  @user.save
  @user.errors[:login].first.should == "cannot contain a period at the start or end"
  @user = Factory.build(:user, :login => 'Admin_f00.')
  @user.save
  @user.errors[:login].first.should == "cannot contain a period at the start or end"
end

it "should not allow white space or \ / \" [ ] : | < > + = ; , ? * @" do
  @user = Factory.build(:user)
  @user.login = "user _1"
  @user.save
  @user.errors[:login].first.should == "cannot contain any whitespace"
  ["\\", '/', '"', '[', ']', ':', '|', '<', '>', '+', '=', ';', ',', '?', '*', '@', "'"].each do |char|
    @user.login = "user_1" << char
    @user.save
    @user.errors[:login].first.should == "can only contain letters number and special these special characters \"-_.\""
  end
end

Simple Rails 3 Validator for Unchangeable Attributes

Here is a simple Rails validator that you can use to ensure an attribute of a model cannot be changed once it has been assigned a value.

1
2
3
4
5
6
7
8
9
10
class UnchangeableValidator < ActiveModel::EachValidator
  def validate_each(object, attribute, value)
    if !object.new_record? && value.present?
      original = object.class.send(:where, "id = #{object.id}").select("id, #{attribute.to_s}").first
      if original.send(attribute) != value
        object.errors[attribute] << (options[:message] || "cannot be changed once assigned")
      end
    end
  end
end

You can use it this way:

1
validates :email, :unchangeable => true, :presence => true

Here are some specs to prove it works:

1
2
3
4
5
it "should not allow you to change an email for an existing record" do
  @user.email = "test@test.com"
  @user.save
  @user.errors[:email].first.should == "cannot be changed once assigned"
end

Generate Beautiful Gradients Using Javascript

I use Highcharts an excellent charting library built in JavaScript for much of my data visualization and analytic work. However, one thing that has always bugged me is their collection of default series colors, which are a bit limp. Highcharts does provide an easy way to manually override their defaults with your own list of colors. Unfortunately, it’s not always possible to know in advance how many series will be supplied to the chart object. In the cases where there are more series than there are manually supplied colors the Highcharts default colors will start to leak into your charts and ruin the visual aesthetics. I wrote the gradient generator to produce a range of colors based on the parameters you supply. You simply supply start and stop colors and the number of steps in between, and you’ll be sure to have enough colors for your series.

Even though I made this generator for a need I had in highcharts, you can use it anywhere you want a uniform sequence of colors. Here is how I use it in Highcharts:

1
2
3
4
5
6
7
8
9
10
11
12
var seriesColors = gradientFactory.generate({
    from: "#0000FF",
    to: ""#FF0000"
    stops: chartConfig.series.length
})
$.each(chartConfig.series,
function(i, v) {
    seriesOptions.push({
        color: seriesColors[i]
        // other attributes ...
    });
});

Here is the code for the Gradient Factory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
gradientFactory = (function() {
    var _beginColor = {
        red: 0,
        green: 0,
        blue: 0
    };
    var _endColor = {
        red: 255,
        green: 255,
        blue: 255
    };
    var _colorStops = 24;
    var _colors = [];
    var _colorKeys = ['red', 'green', 'blue'];
    var _rgbToHex = function(r, g, b) {
        return '#' + _byteToHex(r) + _byteToHex(g) + _byteToHex(b);
    };
    var _byteToHex = function(n) {
        var hexVals = "0123456789ABCDEF";
        return String(hexVals.substr((n >> 4) & 0x0F, 1)) + hexVals.substr(n & 0x0F, 1);
    };
    var _parseColor = function(color) {
        if ((color).toString() === "[object Object]") {
            return color;
        } else {
            color = (color.charAt(0) == "#") ? color.substring(1, 7) : color;
            return {
                red: parseInt((color).substring(0, 2), 16),
                green: parseInt((color).substring(2, 4), 16),
                blue: parseInt((color).substring(4, 6), 16)
            };
        }
    };
    var _generate = function(opts) {
        var _colors = [];
        var options = opts || {};
        var diff = {
            red: 0,
            green: 0,
            blue: 0
        };
        var len = _colorKeys.length;
        var pOffset = 0;
        if (typeof(options.from) !== 'undefined') {
            _beginColor = _parseColor(options.from);
        }
        if (typeof(options.to) !== 'undefined') {
            _endColor = _parseColor(options.to);
        }
        if (typeof(options.stops) !== 'undefined') {
            _colorStops = options.stops;
        }
        _colorStops = Math.max(1, _colorStops - 1);
        for (var x = 0; x < _colorStops; x++) {
            pOffset = parseFloat(x, 10) / _colorStops;
            for (var y = 0; y < len; y++) {
                diff[_colorKeys[y]] = _endColor[_colorKeys[y]] - _beginColor[_colorKeys[y]];
                diff[_colorKeys[y]] = (diff[_colorKeys[y]] * pOffset) + _beginColor[_colorKeys[y]];
            }
            _colors.push(_rgbToHex(diff.red, diff.green, diff.blue));
        }
        _colors.push(_rgbToHex(_endColor.red, _endColor.green, _endColor.blue));
        return _colors;
    };
    return {
        generate: _generate
    };
}).call(this);

Here are a few Jasmine Specs to prove it works:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
describe("Gradient Generator",
function() {
  it("should generate a series of gradient colors",
  function() {
      expect($.hh.plugins.gradientFactory.generate({
          from: '#aed0ee',
          to: '#2a5980',
          stops: 2
      })).toEqual(['#AED0EE', '#2A5980']);
      expect($.hh.plugins.gradientFactory.generate({
          from: '#000000',
          to: '#999999',
          stops: 10
      })).toEqual(['#000000', '#111111', '#222222', '#333333', '#444444', '#555555', '#666666', '#777777', '#888888', '#999999']);
  });
});

A big tip of the hat to my friend jim bumgardner, who i cribbed the color conversation functions from.

Introducing ActsAsModerated

Periodically sites that incorporate user generated content need a way to moderate the incoming stream before publishing it to the site. Typically this is accomplished by putting the content in a queue and allowing moderators to explicitly accept or reject content. I needed such functionality for a site I was working on so I wrote the “ActsAsModerated” plugin which allows specific columns of a model to be audited by a moderator at some later point.

ActsAsModerated is good for:

  • spot-checking user generated content
  • being notified when new content is created
  • tracking changes within a record
  • spam checking

Moreover, this plugin allows for custom callbacks and validations around the moderation event. This flexibility gives developers the ability to augment the moderation flow by stacking custom rules as needed. For example, a developer write code to assign a reputation score to a user and increment that score for every non-spam contribution they make. This would mean that you could auto approve any content that is made by a user above a certain score, thereby reducing the workload on moderators. I have been using this plugin for over a year on a very high-volume site and it has held up quite well, and so I thought I’d share it with the rest of the Ruby on Rails community.

Download & Install

For those with ADD you can find acts_as_moderated here: https://github.com/heavysixer/acts_as_moderated

Setup

Create the moderated_records table like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
create_table "moderation_records", :force => true do |t|
  t.integer  "recordable_id"
  t.string   "recordable_type"
  t.integer  "state_id",             :default => 0
  t.integer  "decision_id",          :default => 0
  t.boolean  "flagged",              :default => false
  t.integer  "moderator_id"
  t.string   "reason"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.text     "inspected_attributes"
  t.boolean  "rejected",             :default => false
end

add_index "moderation_records", ["decision_id"], :name => "index_moderation_records_on_decision_id"
add_index "moderation_records", ["flagged"], :name => "index_moderation_records_on_flagged"
add_index "moderation_records", ["moderator_id"], :name => "index_moderation_records_on_moderator_id"
add_index "moderation_records", ["recordable_id", "recordable_type"], :name => "index_moderation_records_on_recordable_id_and_recordable_type"
add_index "moderation_records", ["rejected"], :name => "index_moderation_records_on_rejected"
add_index "moderation_records", ["state_id"], :name => "index_moderation_records_on_state_id"

Usage

ActsAsModerated has two integration points. The first is within the model(s) to be moderated, which can be done like so:

1
2
3
class Comment < ActiveRecord::Base
  acts_as_moderated :body
end

The second integration point is the class that acts as the moderator. Typically this is some user or account class. The idea is that behind this integration point is to create an audit trail for decisions made by the moderator if you ever need to watch the watcher.

1
2
3
  class Account < ActiveRecord::Base
    acts_as_moderator
  end

The plugin also supports an after_moderation callback on the record being moderated, which you can use to take action based on what the moderator did. For example:

  • Delete the record if it is inappropriate or spam
  • Email the content creator that their content has been approved / denied

There are several dynamically created methods added to every acts_as_moderated class which moderators can use as a shortcut for making decisions. For example where @moderator is an object with acts_as_moderator applied:

1
2
  @comment.marked_spam_by_moderator(@moderator)
  @comment.marked_scam_by_moderator(@moderator, :reason => 'this is obviously a scam, please delete.')

Default Ordering, Callbacks & Flagging

The ModerationRecord class has a named_scope called queue, which will return records sorted oldest to newest. However, if you flag a record it will be returned first regardless of its age relative to unflagged records. This is useful if you want to ensure that moderators see potentially dangerous records first. A good way to flag a record is using the after_moderated callback for example:

1
2
3
4
5
6
7
class Comment < ActiveRecord::Base
  acts_as_moderated :body

  def after_moderated(moderation_record)
    moderation_record.flag! if body =~ /viagra/i
  end
end

The moderation record will attempt to make callbacks on the model being moderated after a record is first created after_moderation and when a moderator rejects a record after_rejection here is an example of what they might do:

1
2
3
4
5
6
7
8
9
10
11
class Story < ActiveRecord::Base
  acts_as_moderated :body

  def after_moderated(moderation_record)
    update_attribute(:moderated, true)
  end

  def after_rejection(moderation_record)
    update_attribute(:rejected, true)
  end
end

Skipping moderation

The moderation plugin adds an attr_accessor called skip_moderation, which when set to true will prevent a moderation record from being created for that instance of a save. This is useful if you need to create records programmatically, which don’t need to be moderated initially but will need to be moderated at some later point. For example:

1
2
  @story = ModeratedStory.create #creates a moderation record
  @story = ModeratedStory.create(:skip_moderation => true) #does not create a moderation record

A word to the wise however, since this attr_accessor prevents records from being moderated you will want to protect it from mass_assignment in your model.

Known Bugs

Presently if you use the :always_moderate flag on a STI model it will produce a never-ending series of record updates. I’ll keep working on this bug, in the meantime please do investigate!

2011 Heaven - Awesome Sauce From the Year That Was

Soon the media waves will be jammed with “Best Of” lists as the last days of 2011 are ripped off the calendar. To get ahead of the curve i’ve decided to create a list of my favorite things from 2011, in no particular order.

Most Helpful Coders

I have a tendency to be a help vampire occasionally, it is a urge i try and suppress where possible. However, I am addicted to smart people and that often leads me down the path of ruination. The people on this list are more helpful and patient than I thought humanly possible. They were always available to answer my often hair-brained and/or panic-stricken questions. This list is not ranked in any particular order:

  • Wayne E. Seguin - For bailing me out on RVM / SM nonsense
  • Michal Papis - For bailing me out on RVM / SM nonsense
  • Vince Toms - For bailing me out on iPhone, Unix, freeBSD and just about any other technical task you can think of

Best Purchases of 2011

  • Breville Barista Express BES860XL machine with grinder I cannot recommend this machine highly enough. I was a barista all through college and have always had a distain for at home expresso machines. The real beauty of this machine is that it gets enough pressure to pull a proper shot, and has a professional grade steam wand, and grinder that can be fine-tuned to boot.
  • Where Good Ideas Come From I am a Steven Johnson fan-boy and his latest offering does not disappoint. It is an enthralling trip through the environments, histories and frames of mind that spark the idea of the next big thing.

Best Audio

Best Time Sink

CSS Anti-patterns

Over the last month of two I have been working on a large enterprise Rails site. The backend has been implemented by a core group of developers but the frontend CSS and HTML have been handed off several times. As I worked to refactor some of the views I noticed several anti-patterns reoccurring in the code that I thought I would highlight and propose work arounds. While all of the examples I present in this file are extracted directly from the site in question I have seen them occur over and over again on other projects.

1. Unclear Naming Conventions

If a tag in an HTML page performs a task then a good CSS name should describe what the job is. It should have semantic value and should not where possible describe only visual attributes.

Anti-pattern Example
1
2
3
4
.teal {
color: #1EC5E9;
font-style: italic
}

This class name gives very little semantic value to tag it is applied to. The class “teal” only describes the visualness of the tag. Furthermore this name is only relevant while the visual design of the page stays the same. If the design changes then every instance of the “teal” class will need to be removed throughout the codebase if the color changes. What is worse is if the CSS attributes within the class are updated without changing the name then the name becomes meaningless. A better approach would be to understand what the teal color is for. If it is meant to draw emphasis to a bit of text then choose a name like “highlight” so that it is more clear what job it is to perform.

2. Invalid Declarations

When writing CSS periodically run your classes through a CSS validator to ensure its syntax is correct. The problem with invalid declarations is that they may not break the tag in question but may corrupt the rest of the cascade causing classes defined later in the file from working. Whenever I am hunting down a CSS error the first thing I do is ensure I am working off a valid document. I can’t tell you how many times just fixing the code in one part of the file fixed mystery bugs that appeared lower in the document.

3. Unnecessary verboseness

Where possible try to compact your CSS attributes into one line:

Anti-pattern Example
1
2
3
4
  background: #2175BE;
  background-image: url(../images/bottomshadownav.png);
  background-position: bottom;
  background-repeat: repeat-x;
Improved Version
1
  background: #2175BE url(../images/bottomshadownav.png) repeat-x bottom;

4. Overly Specific Classes

I strive to make CSS classes do only one thing. That does not mean that my classes only have only one attribute declaration, but it means that I try to assign them only one job to perform. This allows me to build up complex behaviors and visuals to html tags by combining several simple CSS classes. Resist the urge to add properties to your class that doesn’t describe the job it does. This will also make your HTML more clear to read, and easier to extend. Consider the two examples below:

Anti-pattern Example
1
2
3
4
  .slider {
    height: 27px;
    float: left;
  }
Improved Version
1
2
3
4
5
6
  .slider {
    height: 27px;
  }
  .left {
    float: left;
  }

5. Browser Specific Overrides

Where possible resist the urge to place browser specific hacks in the master CSS file. The proper approach is to peel these classes off into an override stylesheet that will be loaded only when the user is using that particular browser. The reason we put override classes in their own file is for two reasons. It simplifies the master CSS file, and gives the developer an easy way to eliminate CSS from the site when the site no longer needs to support a deprecated browser.

Anti-pattern Example
1
2
3
  .decile img {
  _margin: 0px /* IE 6 Hack */
  }

6. Needless Duplication

Part of writing good CSS is to generalize common tasks where possible. In the example below you’ll notice that both classes are essentially the same.

Anti-pattern Example
1
2
3
4
5
6
7
8
  .comparator a {
  margin-right: 3px;
  padding: 3px 13px 3px 13px;
  }
  .ambition a {
  padding: 3px 13px 3px 13px;
  margin-right: 3px
  }
Improved Version
1
2
3
4
  a.link {
    padding: 3px 10px 3px 10px;
    margin-right: 3px
  }

7. Overuse of exclamation point

The “!” override is a brute force method to ensure an attribute of your class cannot be overwritten. This should be used as a last resort and not to mask poor design or abstraction of classes. Overuse of the exclamation point is like a shouting match between all your CSS classes, almost never ends well.

8. “Cargo Culting” through Copy + Paste

Copying CSS en masse and pasting it into the document without making fundamental changes to the class can be a sign of disconnect between the designer and developer or a signal that the developer / designer doesn’t know (or care) about how the copied CSS works. Bulk copying CSS creates deadweight in the CSS file and makes it harder to maintain and extend.

In the first case where a developer is implementing a design given to them by a front-end developer it is entirely possible the developer won’t know how the CSS works. Later, they may find themselves needing to replicate a style elsewhere in the site. Often this means they bulk copy several classes and paste them elsewhere in the document. Typically, they make a trivial changes needed e.g. change the font-size and then rename the class. The proper approach would be to abstract the first class so that it is more generalized and can service both instances of how it needs to be implemented.

However, I found several examples where entire blocks of code appeared three and four times in the same file with absolutely no difference! This is absolutely unacceptable in a professional product.

9. Unused Classes

With any site under constant development it is easy for classes to become unused within the html. Developers and designers must make it part of their development process to remove unused classes from the CSS otherwise the site can become bogged down with all the deadweight inside the CSS file. There are projects like “deadweight” which are specifically dedicated to helping you prune your CSS of unused code.

10. Needless Namespacing

Namespacing is used to change the way a child class works when its parent class changes. This is a great way to keep your HTML and CSS flexible because it means that a single change to the parent element can change the look and feel of all the containing children. However, if you over namespace your CSS by binding it to specific html tags where not absolutely needed you make the CSS brittle and hard to maintain.

Anti-pattern Example
1
2
3
4
5
  .qipp-inputs table tr.metric td.metric_name {
  border-bottom: 1px dotted #B6CBD2;
  padding-left: 20px;
  text-align: left !important;
  }
Improved Example
1
2
3
4
5
  .qipp-inputs .metric td.metric_name {
  border-bottom: 1px dotted #B6CBD2;
  padding-left: 20px;
  text-align: left !important;
  }

I welcome any discussion or feedback on these patterns and would love to improve this document where possible. What anti-patterns have you seen in CSS files you’ve worked on?

Transactions in Rails

Recently I was tasked to write tests for the transactions of an existing application. This gave me the opportunity to learn more about the codebase, while also improving the test coverage. Generally, most of the transaction code looked fine. However, there were some instances where transactions were used incorrectly or inefficiently. I assumed this is just because of a misunderstanding of how transactions work within Rails, and so I thought iʼd take some time and give an overview of the common errors I found and some best practices for using transactions in Rails.

Let me also state at the beginning that most of these examples are not my own, they come directly from the Rails source, which give example usages of applying transactions RTFM FTW.

http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

Reasons for transactions

We use transactions as a protective wrapper around SQL statements to ensure changes to the database only occur when all actions succeed together. Transaction help the developer enforce data integrity within the application. The canonical example of transactions is the banking method where funds are withdrawn from one account and deposited into the next. If either of these steps fail, then the entire process should be reset. This example can be described in code this way:

1
2
3
4
ActiveRecord::Base.transaction do
  david.withdrawal(100)
  mary.deposit(100)
end

In Rails transactions are available as class and instance methods for all ActiveRecord models. This means either of these approaches are equally valid:

1
2
3
4
5
6
7
8
9
10
11
Client.transaction do
  @client.users.create!
  @user.clients(true).first.destroy!
  Product.first.destroy!
end

@client.transaction do
  @client.users.create!
  @user.clients(true).first.destroy!
  Product.first.destroy!
end

You may also notice that there are several different model classes referenced in these transactions. It is perfectly fine to mix model types inside a single transaction block. This is because the transaction is bound to the database connection not the model instance. As a rule, transactions are only needed when changes to multiple records must succeed as a single unit. Additionally, Rails already wraps the #save and #destroy methods in a transaction, therefore a transaction is never needed when updating a single record.

Transaction Rollback Triggers

Transactions reset the state of records through a process called a rollback. In Rails, rollbacks are only triggered by an exception. This is a crucial point to understand; I saw several transaction blocks that would never rollback because the containing code could not throw an exception. Here I have slightly modified our banking example from before:

1
2
3
4
ActiveRecord::Base.transaction do
  david.update_attribute(:amount, david.amount -100)
  mary.update_attribute(:amount, 100)
end

In Rails #update_attribute is designed not to throw an exception when an update fails. It returns false, and for this reason you should ensure that the methods used throw an exception upon failure. A better way to write the previous example would be:

1
2
3
4
ActiveRecord::Base.transaction do
  david.update_attributes!(:amount => -100)
  mary.update_attributes!(:amount => 100)
end

Note: The bang modifier (!) is a rails convention for a method which will throw an exception upon failure.

I also saw examples in the code where records were found inside of transactions using magic finders #find_by. By design magic finders will return nil when no record is returned. This is in contrast to the normal #find_ method which throws an ActiveRecord::RecordNotFound exception. Consider this example:

1
2
3
4
5
6
7
ActiveRecord::Base.transaction do
  david = User.find_by_name("david")
  if(david.id != john.id)
    john.update_attributes!(:amount => -100)
    mary.update_attributes!(:amount => 100)
  end
end

Do you see the logic error? The nil object will have an id attribute and it will mask the fact that the desired record was not returned. While this doesn’t cause an error in the transaction it will still lead to a loss of data integrity, because the application is updating where it shouldn’t. Remember a transaction defines a block of code which must succeed as an atomic unit. This means we should raise an exception when a logical dependency like this is not met. Here is how the code should be written:

1
2
3
4
5
6
7
8
ActiveRecord::Base.transaction do
  david = User.find_by_name("david")
  raise ActiveRecord::RecordNotFound if david.nil?
  if(david.id != john.id)
    john.update_attributes!(:amount => -100)
    mary.update_attributes!(:amount => 100)
  end
end

When the exception occurs it will be raised outside of the transaction block after the rollback has completed. Your application code must be ready to catch this exception as it bubbles up through the application stack.

It is also possible to invalidate a transaction without raising an exception that will propagate upwards by using ActiveRecord::Rollback. This special exception allows you to invalidate a transaction and reset the database records without needing to rescue elsewhere in your code.

When To Use Nested Transactions

One of the common mistakes I saw in the codebase is the misuse or overuse of nested transactions. When you nest a transaction inside another transaction this has the effect of making the child transaction a part of the parent. This can have surprising results, take this example from the Rails API documentation:

1
2
3
4
5
6
7
User.transaction do
  User.create(:username => 'Kotori')
  User.transaction do
    User.create(:username => 'Nemu')
    raise ActiveRecord::Rollback
  end
end

As mentioned previously ActiveRecord::Rollback does not propagate outside of the containing transaction block and so the parent transaction does not receive the exception nested inside the child. Since the contents of the child transaction are lumped into the parent transaction both records are created! I find it easier to think of nested transactions like the child who dumps its contents into the parent container, leaving the child transaction empty.

To ensure a rollback is received by the parent transaction you must add the :requires_new => true. option to the child transaction. Again using the example from Rails source you would trigger a parent’s Rollback like this:

1
2
3
4
5
6
7
User.transaction do
  User.create(:username => 'Kotori')
  User.transaction(:requires_new => true) do
    User.create(:username => 'Nemu')
    raise ActiveRecord::Rollback
  end
end

A transaction only acts upon the current database connection. If your application writes to multiple databases at once you will need to wrap the method inside a nested transaction. For example:

1
2
3
4
5
6
Client.transaction do
  Product.transaction do
    product.buy(@quantity)
    client.update_attributes!(:sales_count => @sales_count + 1)
  end
end

Callback around Transactions

As mentioned previously #save and #destroy are wrapped inside a transaction. This means that callbacks like #after_save is still part of the active transaction which might still be rolled back! Therefore if you want code to execute that is guaranteed to execute outside of the transaction use one of the transaction specific callbacks like #after_commit or #after_rollback.

Transaction Gotchas

Do not catch an ActiveRecord::RecordInvalid exception inside a transaction because the this exception invalidates the transaction on some databases like Postgres. Once the transaction has been invalidated you must restart it from the beginning for it to work correctly.

When testing rollbacks or transaction related callbacks that happen after rollback you will want to turn off transactional_fixtures which are on by default in most test frameworks.

Common Anti-patterns To Avoid

  1. Using a transaction when only a single record is updated
  2. Needlessly nesting transactions
  3. Transactions that contain code, which won’t cause a rollback
  4. Use of transactions in a controller