Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Pivotal Labs

Parallelize Your RSpec Suite

Pivotal Labs
Friday, May 8, 2009

We all have multi-core machine these days, but most rspec suites still run in one sequential stream. Let’s parallelize it!

The big hurdle here is managing multiple test databases. When multiple specs are running simultaneously, they each need to have exclusive access to the database, so that one spec’s setup doesn’t clobber the records of another spec’s setup. We could create and manage multiple test database within our RDBMS. But I’d prefer something a little more … ephemeral, that won’t hang around after we’re done, or require any manual management.

Enter SQLite’s in-memory database, which is a full SQLite instance, created entirely within the invoking process’s own memory footprint.

(Note #1: the gist for this blog is at http://gist.github.com/108780)

(Note #2: The following strategy is relatively well-known, but I thought it might be useful for Pivots-and-friends to see exactly how one Pivotal project has used this tactic for a big speed win.)

Here’s the relevant section of our config/database.yml:

test-in-memory:
  adapter: sqlite3
  database: ':memory:'

Next, we need a way to indicate to the running rails process that it should use the in-memory database. We created an initializer file, config/intializers/in-memory-test.db:

def in_memory_database?
  ENV["RAILS_ENV"] == "test" and
    ENV["IN_MEMORY_DB"] and
    Rails::Configuration.new.database_configuration['test-in-memory']['database'] == ':memory:'
end

if in_memory_database?
  puts "connecting to in-memory database ..."
  ActiveRecord::Base.establish_connection(Rails::Configuration.new.database_configuration['test-in-memory'])
  puts "building in-memory database from db/schema.rb ..."
  load "#{Rails.root}/db/schema.rb" # use db agnostic schema by default
  #  ActiveRecord::Migrator.up('db/migrate') # use migrations
end

Note that in the above, we’re initializing the in-memory database with db/schema.rb, so make sure that file is up-to-date. (Or, you could uncomment the line that runs your migrations.)

Let’s give that a whirl:

$ IN_MEMORY_DB=1 RAILS_ENV=test ./script/console
Loading test environment (Rails 2.3.2)
connecting to in-memory database ...
building in-memory database from db/schema.rb ...
-- create_table("users", {:force=>true})
   -> 0.0065s
-- add_index("users", ["deleted_at"], {:name=>"index_users_on_deleted_at"})
   -> 0.0004s
-- add_index("users", ["id", "deleted_at"], {:name=>"index_users_on_id_and_deleted_at"})
   -> 0.0003s

...

>>

Super, we can see that the database is being initialized our of our schema.rb, and we get our console prompt. We’re ready to roll!

But, running this:

IN_MEMORY_DB=yes spec spec

will still only result in a single process, albeit one running off a database that’s entirely in-memory. We want parallelization!

The final step is a script that will run your spec suite for you. You may need to edit this for your particular situation, but then again, maybe not.

#  spec/suite.rb

require "spec/spec_helper"

if ENV['IN_MEMORY_DB']
  N_PROCESSES = [ENV['IN_MEMORY_DB'].to_i, 1].max
  specs = (Dir["spec/**/*_spec.rb"]).sort.in_groups_of(N_PROCESSES)
  processes = []

  interrupt_handler = lambda do
    STDERR.puts "caught keyboard interrupt, exiting gracefully ..."
    processes.each { |process| Process.kill "KILL", process }
    exit 1
  end

  Signal.trap 'SIGINT', interrupt_handler
  1.upto(N_PROCESSES) do |j|
    processes << Process.fork {
      specs.each do |array|
        if array[j-1]
          require array[j-1]
        end
      end
    }
  end
  1.upto(N_PROCESSES) { Process.wait }

else
  (Dir["spec/**/*_spec.rb"]).each do |file|
    require file
  end
end

Then, you simply run IN_MEMORY_DB=2 spec spec/suite.rb to run two parallel processes. Increase the number on larger machines for better results!

There’s room for improvement here, notably in the naive method used to allocate the spec files to processes, but even as simple as this method is, our spec suite runs in about half the time it used to, on a dual-core machine.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup 3/26/2009: Testing Request Headers When Request Object Is Frozen

Pivotal Labs
Thursday, March 26, 2009

Ask for Help

“How do you test request headers? The request object is frozen…”

The team is using rspec to test an OAuth implementation and needs better access to the request object.

  • Possibly modify the request environment prior to running the test -or-
  • Instantiate a new, non-frozen request object.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup 3/25/2009: Branches + JSUnit + CI + IE = :-(

Pivotal Labs
Wednesday, March 25, 2009

Interesting Things

  • Branches + JsUnit + CI + IE = :-( : Apparently it is difficult to manage IE’s cache in CI. One project apparently has a bat file on CI that clears the cache every 30 minutes. Another team solved this by making the cache directory read only. Often browser/OS combinations have some technique for disabling caching.
  • Test Swarm Alpha: this is a crowd sourced javascript testing solution (think seti@home for javascript testing) being developed by John Resig.

Ask for Help

“AR attribute appears to be skipped by text field helper?!” Apparently the model method is bypassed by the text field helper if a column of the same name is present in the underlying table. This was experienced in Rails 2.2.

Others have apparently experienced this in the past but a clear answer did not surface.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Joe Moore

DRY, Targeted, and Reusable Testing of ActiveRecord Extensions

Joe Moore
Wednesday, March 18, 2009

At Pivotal, we are passionate about test driven development, keeping things DRY, and writing readable and understandable code. Satisfying all of these desires can be challenging, especially when writing test code. In particular, ActiveRecord extensions present several challenges: which models using an extension should we test? How do we both test our extension in isolation while also testing all model’s usage of that extension? Is it even worth it?

The answer is yes, it is worth it, and it’s also fairly easy, readable, understandable, and DRY. I will present both a common problem and a solution, using a cumulation of technologies and techniques from multiple Pivotal projects, in particular using acts_as_fu to create laser-targeted, isolated, and disposable ActiveRecord models for testing extensions and RSpec shared behaviors to minimize the amount of duplicated test code.

Often we find common patterns in ActiveRecord models and we wish to share that functionality by mixing in a module of shared code, or even mixing that module in to ActiveRecord::Base itself. How should we go about testing these ActiveRecord extensions? Not only do we want to test the extension, but also test that models using that extension are doing so properly. We’ve blogged about dynamically creating ActiveRecords to test extensions in the past, but Pivot Pat Nakajima’s acts_as_fu plugin is a far better tool for this.

The Setup

Let’s say we have three models: Pivotal, Lab, and Pivot. Both a Pivot and a Lab belongs_to Pivotal, have a name, nickname, some common validation, etc:

# app/models/pivotal.rb
class Pivotal < ActiveRecord::Base
end

# app/models/lab.rb
class Lab < ActiveRecord::Base
  RANDOM_NICKNAMES = ["New Hotness", "LOL-Cat Factory", "Tweet Machine"]
  belongs_to :pivotal
  validates_presence_of :name

  def nickname
    "#{self.name}, 'The #{RANDOM_NICKNAMES[rand(RANDOM_NICKNAMES.length)]}'"
  end
end

# app/models/pivot.rb
class Pivot < ActiveRecord::Base
  RANDOM_NICKNAMES = ["New Hotness", "LOL-Cat Factory", "Tweet Machine"]
  belongs_to :pivotal
  validates_presence_of :name

  def nickname
    "#{self.name}, 'The #{RANDOM_NICKNAMES[rand(RANDOM_NICKNAMES.length)]}'"
  end
end

The specs for Lab and Pivot might look like the following:

# spec/models/lab_spec.rb
describe Lab do
  before(:each) do
    @lab = Lab.new
  end

  it "should require name" do
    @lab.should have(1).errors_on(:name)
    @lab.name = 'Stealth Startups'
    @lab.should have(0).errors_on(:name)
  end

  it "should generate a random nickname" do
    @lab.name = 'Stealth Starup'
    @lab.nickname.should_not be_blank
    @lab.nickname.should include(@lab.name + ", 'The ")
  end

  it "should belong to Pivotal" do
    @lab.should respond_to(:pivotal)
    @lab.should respond_to(:pivotal=)
    @lab.should respond_to(:pivotal_id)
    @lab.should respond_to(:pivotal_id=)
  end
end

# spec/models/pivot_spec.rb
describe Pivot do
  before do
    @pivot = Pivot.new
  end

  it "should require name" do
    @pivot.should have(1).errors_on(:name)
    @pivot.name = 'Joe Moore'
    @pivot.should have(0).errors_on(:name)
  end

  it "should generate a random nickname" do
    @pivot.name = 'Joe Moore'
    @pivot.nickname.should_not be_blank
    @pivot.nickname.should include(@pivot.name + ", 'The ")
  end

  it "should belong to Pivotal" do
    @pivot.should respond_to(:pivotal)
    @pivot.should respond_to(:pivotal=)
    @pivot.should respond_to(:pivotal_id)
    @lab.should respond_to(:pivotal_id)
  end
end

DRYing Up the Models

Yuck, look at all that duplication! Let’s start eliminating it by pulling the common model code into an ActiveRecord extension named belongs_to_pivotal:

# lib/belongs_to_pivotal.rb
module BelongsToPivotal
  RANDOM_NICKNAMES = ["New Hotness", "LOL-Cat Factory", "Tweet Machine"]
  module ClassMethods
    def belongs_to_pivotal
      belongs_to :pivotal
      validates_presence_of :name

      instance_eval do
        include BelongsToPivotalInstanceMethods
      end
    end
  end

  module BelongsToPivotalInstanceMethods
    def nickname
      "#{self.name}, 'The #{BelongsToPivotal::RANDOM_NICKNAMES[rand(BelongsToPivotal::RANDOM_NICKNAMES.length)]}'"
    end
  end

  def self.included(base)
    base.extend(ClassMethods)
  end
end

Now our Models look like this:

# app/models/lab.rb
class Lab < ActiveRecord::Base
  belongs_to_pivotal
end

# app/models/pivot.rb
class Pivot < ActiveRecord::Base
  belongs_to_pivotal
end

You’ll need to add “ActiveRecord::Base.send :include, BelongsToPivotal” to config/initializers/new_rails_defaults.rb or some other initializer.

Testing the Extension with acts_as_fu

The models are looking better, but what about the specs? In the “old days” I would create a spec named belongs_to_pivotal_spec.rb and use one of the two Models in that spec. But, when you do that, you get all the the “baggage” from that Model, such as any other methods, associations, inherited methods and properties, etc. Let’s use acts_as_fu to write a spec that tests BelongsToPivotal in isolation.

# spec/lib/belongs_to_pivotal_spec.rb
describe BelongsToPivotal do
  before(:all) do
    # Using acts_as_fu to create a model specifically for our extension
    build_model :belongs_to_pivotal_models do
      # we will need these columns in the database
      string :name
      integer :pivotal_id

      # Call our extension here
      belongs_to_pivotal
    end
  end

  before(:each) do
    @pivotal_model = BelongsToPivotalModel.new
  end

  # Look, it's all of the model specs!
  it "should require name" do
    @pivotal_model.should have(1).errors_on(:name)
    @pivotal_model.name = 'Pivotal Model'
    @pivotal_model.should have(0).errors_on(:name)
  end

  it "should generate a random nickname" do
    @pivotal_model.name = 'Pivotal Model'
    @pivotal_model.nickname.should_not be_blank
    @pivotal_model.nickname.should include(@pivotal_model.name + ", 'The ")
  end

  it "should belong to Pivotal" do
    @pivotal_model.should respond_to(:pivotal)
    @pivotal_model.should respond_to(:pivotal=)
    @pivotal_model.should respond_to(:pivotal_id)
    @pivotal_model.should respond_to(:pivotal_id=)
  end
end

Now that our ActiveRecord extension is well tested, how do we make sure that our two models are actually using it? One technique is to check that each model responds to the specific methods added by our extension:

#spec/models/lab_spec.rb
describe Lab do
  ...
  it "should belong_to_pivotal" do
    @lab.should respond_to(:pivotal)
    @lab.should respond_to(:pivotal=)
    @lab.should respond_to(:pivotal_id)
    @lab.should respond_to(:pivotal_id=)
    @lab.should respond_to(:name)
    @lab.should respond_to(:name=)
    @lab.should respond_to(:nickname)
  end
  ...

This does not feel very satisfying. We are duplicating some of the tests from belongs_to_pivotal_spec.rb and not verifying that we are getting the validations. A crazy coincidence could result in these methods all being defined without actually using our extension.

Another technique, though some would call it a hack, is to provide a hook within the extension itself so we can check for it later:

# lib/belongs_to_pivotal.rb
module BelongsToPivotal
  ...
  module ClassMethods
    ...
    # We can check this to see if a model uses this extension
    def belongs_to_pivotal?
      self.included_modules.include?(BelongsToPivotalInstanceMethods)
    end
  end
  ...
end

Let’s update belongs_to_pivotal_spec.rb to test this method:

# spec/lib/belongs_to_pivotal_spec.rb
describe BelongsToPivotal do
  before(:all) do
    # Using acts_as_fu to create a model specifically for our extension
    build_model :belongs_to_pivotal_models do
      ...
    end

    # Create a model that does not use our extension
    build_model :never_belongs_to_pivotal_models do
      # do nothing
    end
  end
  ...
  it "should know if it belongs_to_pivotal" do
    BelongsToPivotalModel.belongs_to_pivotal?.should be_true
    NeverBelongsToPivotalModel.belongs_to_pivotal?.should be_false
  end
end

#spec/models/lab_spec.rb
describe Lab do
  it "should belong_to_pivotal" do
    Lab.belongs_to_pivotal?.should be_true
  end
end

#spec/models/pivot_spec.rb
describe Pivot do
  it "should belong to Pivotal" do
    Pivot.belongs_to_pivotal?.should be_true
  end
end

Using RSpec Shared Behaviors

How much further can we go? Notice that our two Model specs are 5 whole lines long! Unacceptable! All kidding aside, we can DRY this up just a bit more by using RSpec’s shared behaviors.

In spec/spec_helper.rb

# spec/spec_helper.rb
describe 'it belongs to pivotal', :shared => true do
  it "should belongs_to_pivotal" do
    described_class.belongs_to_pivotal?.should be_true
  end
end

Now we can use this shared behavior in our specs:

#spec/models/lab_spec.rb
describe Lab do
  it_should_behave_like "it belongs to pivotal"
end

#spec/models/pivot_spec.rb
describe Pivot do
  it_should_behave_like "it belongs to pivotal"
end

I hope that these techniques are helpful. Feel free to post your own!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Run JavaScript in Selenium tests. Easily.

Pivotal Labs
Thursday, March 5, 2009

Here’s the gist of this post: gist.github.com/58876

Ever since I’ve started using Webrat, a lot of the pain of Selenium has gone away
for me. There’s still a little bit of pain though. Part of it is caused by the fact
that it’s harder than it should be to just execute arbitrary bits of JavaScript in
in your current window under test. Well no more. Here’s a helper:

module SeleniumHelpers
  # Execute JavaScript in the context of your Selenium window
  def run_javascript(javascript)
    driver.get_eval <<-JS
      (function() {
        with(this) {
          #{javascript}
        }
      }).call(selenium.browserbot.getCurrentWindow());
    JS
  end

  private

  # If running in regular Selenium context, get_eval is defined on self.
  def driver
    respond_to?(:selenium) ? send(:selenium) : self
  end
end

To use it with Cucumber, do like so:

World do |world|
  world.extend(SeleniumHelpers)
  world
end

To use it with POS, do like so:

class JavaScriptHelperTest < SeleniumTestCase
  include SeleniumHelpers

  # your tests go here...
end

Now what?

Now to run JavaScript in your Selenium window, just call run_javascript. Note
that it’s always going to return a String, so you may have to massage the output
a tad:

checked_boxes_count = run_javascript <<-JS
  jQuery('input[type=checkbox]:checked').size();
JS

checked_boxes_count         # => "3"
checked_boxes_count.to_i   # => 3

Cooler stuff

While Webrat’s DSL for traversing web apps is awesome, I’ve always found the
alternatives (Polonium for example) to not jive well with how I think. They’re
way better than talking directly to Selenium, you’re still locked in to a certain
style. The run_javascript helper makes it easier to write your own helpers that
fit your own style.

module ElementHelpers
  class Element
    def initialize(context, selector)
      @context, @selector = context, selector
    end

    def hide!
      call(:hide)
    end

    def show!
      call(:show)
    end

    def visible?
      call(:is, ':visible') == 'true'
    end

    private

    def call(fn, *args)
      @context.run_javascript <<-JS
        return jQuery(#{@selector.inspect})[#{fn.to_s.inspect}](#{args.map(&:inspect).join(', ')});
      JS
    end
  end

  def locate(selector)
    Element.new(self, selector)
  end
end

Now you can write your tests like so:

class JavaScriptHelperTest < ActiveSupport::TestCase
  include SeleniumHelpers
  include ElementHelpers

  def setup
    @element = locate('#all')
  end

  def test_visible_by_default
    assert @element.visible?
  end

  def test_hide_element
    @element.hide!
    assert ! @element.visible?
  end

  def test_show_element
    @element.hide! # setup
    @element.show!
    assert @element.visible?
  end
end

Credit should go to Brian Takita, since he did most of the hard work and I just wrote a method. Let me
know if you have any issues or ideas with the helper, and may all your tests be green.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

Milligan's Law

Adam Milligan
Sunday, February 8, 2009

I’m not sure a person can name a law after himself, but if I had a law I would want it to be this:

Any non-additive change to non-test code that causes no test failures is a valid change and does not reduce the correctness of the code.

By extension, the first corollary would have to be this:

The full definition of correct behavior of code exists in the tests for that code.

Think about it.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

Silly gents, it is doing a good job (7)

Adam Milligan
Saturday, February 7, 2009

I recently published the article There is no Agile, in which I stated that the principles of ‘Agile’ are nothing more than “a collection of good ideas, based on years of collective experience, for improving how we do our jobs.” As an example, consider testing. Thorough testing is a ubiquitous principle of ‘Agile;’ thus, by logical extension, writing tests is a fundamentally important part of writing software well.

In short, if you write software, and you’re not writing tests, then you’re not doing your job.

I don’t mean not doing your job like a waiter who doesn’t take your order quickly enough; I mean not doing your job like a surgeon who operates without washing his or her hands. As in completely unacceptable performance.

As a corollary, if you’re not writing high quality tests, then you’re doing your job poorly. If you don’t achieve appropriate test coverage then you’re doing your job poorly. If you focus excessively on verification, and ignore the other benefits that tests can provide, then you’re doing your job poorly.

Several authors and accomplished software writers have cataloged the ways that thorough testing will lead to better design, the ability to refactor, improved resilience in the face of changing requirements, fewer defects, etc. In order to justify not writing tests the costs would have to overshadow these advantages.

I can’t think of a valid argument for not writing tests, although I’ve heard many attempts:

  • “It takes too long.” Consider that around 75% of the time spent on average software projects is bug-fixing and maintenance.
  • “This code won’t change.” It will. And it should.
  • “I know my code works.” Will it work with my code? With the code you write six months from now? When the functionality changes? (see above)
  • “It’s a prototype.” Fine. Throw it out when you’re done and use what you learned to write tested code.
  • “It’s not testable.” Unlikely. Far more likely you simply haven’t though of a way to test it.
  • “That’s what QA is for.” This statement shows so little respect for the people you presumably work with it warrants no response.
  • “I can’t be bothered.” McDonald’s is hiring.

In my experience, people who denounce the value of tests are nearly always people with little experience writing tested code. It’s time we stop allowing ourselves to determine how we do our job based on who has the biggest ego, who can shout the loudest, or who is the most resistant to change.

All this leads to the question that every single person reading this should be asking: writing high quality tests can be very difficult; how do we teach that?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Abhijit Hiremagalur

Standup 01/12/2009: require & class reloading, acts_as_fu

Abhijit Hiremagalur
Monday, January 12, 2009

Interesting Things

  • Using ‘require’ explicitly interferes with class reloading in Rails

Frederick Cheung discusses this in more detail here. This might be related to the Selenium + class reloading issues some pivots have experienced in recent weeks. The alternative is to rely on Rails automagic loading or ‘require_dependency’.

  • acts_as_fu makes writing database independent tests for models is easier

Props to pivot Pat Nakajima for creating acts_as_fu.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Chad Woolley

Automated End-to-end Integration Testing for ActiveResource APIs

Chad Woolley
Thursday, January 8, 2009

By popular demand, we’re working on making the Pivotal Tracker API ActiveResource-compliant.

However, there are some quirks that are required to make ActiveResource happy. For example, when you are doing a ‘create’ or ‘update’ request, ActiveResource wants the response location to point to the ‘show’ URL for the new or updated record. For example, here’s an ActiveResource ‘create’ call:

new_story = Story.create(
  :name => "New Story",
  :requested_by => "Dan",
  :description => "Make API ActiveResource compliant")

On the controller, you must add the :location option to the render – you can’t redirect:

render  :xml => xml,
        :location => service_project_story_url(service_id, project_id, @story),
        :status => status

…otherwise, you get this helpful error from ActiveResource:

/Library/Ruby/Gems/1.8/gems/activeresource-2.2.2/lib/active_resource/base.rb:1006
        :in `id_from_response': undefined method `[]' for nil:NilClass (NoMethodError)
        from /Library/Ruby/Gems/1.8/gems/activeresource-2.2.2/lib/active_resource/base.rb:993:in `create'

This is the type of error which you will only catch through end-to-end testing with a real ActiveResource client hitting the running app. When I did the initial spike to see what problems we would run into, I wrote a simple manual script to run against the local development environment, hacking my way to a point which didn’t blow up and I could visually inspect the output:

#!/usr/bin/env ruby
require 'rubygems'
require 'activeresource'
require 'pp'
class Story < ActiveResource::Base
  self.site = "http://localhost:3000/services/v1/projects/1"
  headers['TOKEN'] = '6cfc2055d1df5605241759014b06b232'
end

p "========================== Stories#create ====================================="
new_story = Story.create(:name => "New Story", :requested_by => "Dan", :description => "Make API ActiveResource compliant")
pp new_story

# etc for all other supported API actions...

However, now that we are doing the real non-spike implementation, we want to automate this end-to-end integration testing as part of our Continuous Integration. That way, we’ll ensure that we are fully ActiveResource-compliant (against current and future versions), and that we don’t have any inadvertent regressions due to future API bugfixes/enhancements.

Digging through the internets and rubyonrails-talk list archives turns up some discussion, but no good answers:

  • Thoughtbot blog post on ActiveResource and Testing
  • rubyonrails-talk post on “Testing XML over HTTP in a Rails app”
  • rubyonrails-talk post on “Testing ActiveResource models with HttpMock”

All of these mention using ActiveResource::HttpMock. However, as Eric and Xavier point out, there seem to be drawbacks to this approach. Plus, even if we get it to work, I’m worried the usage of HttpMock might mask some other issues related to authentication handling, or who knows what else. That’s what real integration tests are for. Finally, HttpMock is an undocumented internal method that seems to exist in order to support Rails’ test suite, so it’s probably not a great idea to depend on that long term.

So, we don’t have a great answer yet, but it seems clear that the highest-value, least-risk approach is to hit a real running app over HTTP with a real ActiveResource client.

The current plan is to leverage our existing Selenium RC test environment, which already has support for spinning up and managing a Rails server with the test environment. We can then port the manual spike tests above to automated ones which run as part of the selenium suite under Continuous Integration, and add appropriate assertions. This isn’t optimal, though, because they won’t actually use Selenium RC at all, which may confuse people. However, there’s no sense reinventing the wheel (and adding time to the overall CI build) by spinning up a separate test server instance when we already spin one up for our selenium suite.

Let us know if you have any clever solutions.

– Chad

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

There is no Agile

Adam Milligan
Saturday, January 3, 2009

On occasion, someone will ask me what I do or, more commonly, ask me what Pivotal does. The title on my business card says “Agile Developer,” which nearly inevitably leads to one of a few reactions, almost all of which boil down to this:

“What exactly does Agile mean?”

You know what? It’s a good question. And, after thinking about Agile for several years now, here’s my answer: Agile originally was, and still is, a marketing term.

Allow me to explain.

We’ve been writing software, in one form or another that most people would recognize as such, for some time now. Scott Bain sets the mark at about 30-35 years[1]. Considering the acceleration of technological advancement in the past century, this is a fairly long time.

In the early days, most programs were small, relatively simple, and written by a handful of developers or fewer[2]. So, the general approach to writing them was to put a number of programming language instructions one after another until the computer did the right thing. Eventually.

I believe it was Watts Humphrey who described this type of programming as “ad hoc development.”[3]

Of course, computers got bigger and badder, and software got more complex, and ad hoc quickly stopped scaling. So, people went looking for a new model to stop the hemorrhaging, and they found waterfall. Now, there’s nothing wrong with waterfall for processes that conform to defined process modeling, such as manufacturing Model-Ts, installing sewer lines, or preparing breakfast cereal. But, most people realize these days that it’s not so hot for processes with requirements that may change.

Years passed, and smart people got stuff done by solving different problems in different ways. And, some patterns started to emerge. And, better yet, some truly clever people started to recognize these patterns and write about them and codify them. However, they realized, astutely, that human beings are a hard-headed bunch who don’t like to change their ways. They needed something to shake programmers out of their collective torpor; they needed something flashy to sell; they needed a manifesto. What better thing to offer to an industry plagued by setbacks and missed deadlines than Agile?

To this day the marketing pitch continues; Agile vs. Waterfall, new vs. old, crazy vs. stodgy.

What can we infer from this brief history?

  • The move to waterfall wasn’t motivated by an understanding of the specific challenges of software[4]; it was an attempt to apply a working process, any working process, to an industry that was suffering from far too many failures due to a lack thereof.
  • Many of the principles that have come to be reflected in “agile” processes or “agile” methodologies simply emerged as consistently successful patterns from years of software projects.
  • Over time, a few people have recognized these patterns, studied them, tried to explain them, and expanded upon them to make them even more effective[5].

The conclusion I draw is this: so-called “Agile” is actually nothing more than a collection of good ideas, based on years of collective experience, for improving how we do our jobs as software writers. Or, to put not too fine a point on it, professionalism.

So, if doing “Agile” things means doing your job well, the term ceases to have meaning. As it should. No one should have to sell us good ideas, we should embrace them and have the discipline to stand by them.


[1] Emergent Design: Addison Wesley, 2008

[2] This wasn’t true of all software, of course; the punch card systems that ran the Apollo space program weren’t hacked out by a couple guys in a garage. But, systems like that have their own, mostly time-related, problems.

[3] I loaned out my copy of Winning With Software and can’t immediately find a reference, should anyone care to confirm or deny this credit.

[4] This relates to my rejection of the term “software engineer.” No one really agrees on what writing software is, but we have to call it something. If we call ourselves engineers then we sound like we know what we’re doing. After all, look at all the good stuff in the world that engineers have built.

[5] Pair programming is the classic example of this: code reviews improve code, more frequent code reviews improve code more. How about we code review all code, all the time, as it’s written?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (778)
  • rails (113)
  • testing (87)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (54)
  • techtalk (44)
  • rspec (38)
  • activerecord (29)
  • productivity (29)
  • gogaruco (29)
  • ironblogger (29)
  • git (28)
  • nyc (27)
  • rubymine (25)
  • bloggerdome (22)
  • mobile (22)
  • cucumber (20)
  • process (19)
  • pivotal tracker (19)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • tdd (13)
  • selenium (12)
  • css (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to testing Feed
  1. ←
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >