2.0.0.pre8: final prerelease

Posted 4 months back at The Hobo Blog

I’m proud to announce the release of Hobo 2.0.0.pre8. We’re planning on releasing this as 2.0.0 final in a week or so if no significant issues are found, so please test this release against your applications.

This release has been tested against Rails 3.2.11 on Ruby 1.9.3-p374, Ruby 1.8.7-p378 and JRuby 1.7.2.

Changes

The vast majority of updates to Hobo for 2.0.0.pre8 were to the documentation.

  • The default theme has been changed to Bootstrap

  • MarkdownString will now use Kramdown, RDiscount or Maruku in preference to Bluecloth if these are available.

Minor Changes

  • <sortable-input-many> and <sortable-collection> have been moved to the hobo_jquery_ui gem

  • fix for recognize_page_path when used for non-GET requests

  • fix for AJAX response when no part is specified

  • <view> has gained a force attribute

  • ILIKE is now used for automatic scopes on Postgres

  • hobo_login now respects the return code of its block

  • the user class now works without a lifecycle

  • fixes for non-empty relative url root

Wrestling with Bundler

Posted 4 months back at Dan Manges

I’ve been working on upgrading an app from Rails 2.3 to 3.0. As one of the first steps, I’m converting the app to use Bundler. Currently, all of the gems are in the vendor/gems directory, except for gems which need to be compiled — those are installed as system gems. I ran into some unexpected challenges with the upgrade. Thankfully, a friend had some advice.

Approach

To introduce Bundler as incrementally as possible, I wanted to try to have Bundler load the system gems and the gems from the vendor/gems directory without making any other changes. I didn’t want to have to install Bundler as a system gem, or change Capistrano scripts to run any additional commands as part of the deployment process. I was also hoping that the production runtime environment would remain as much the same as possible. I ran into two issues, both related to groups.

For an example, I’m going to use this Gemfile.

source :rubygems

gem "rails", "2.3.16"

group :development do
  gem "faker", "1.1.2"
  gem "thin", "1.5.0"
end

I was able to follow Bundler's instructions for Rails 2.3 successfully. If you remove the error handling the instructions, you end up with:

require "rubygems"
require "bundler"
Bundler.setup

I wanted to make sure that development gems weren’t loaded in production, so I added a group to the setup call to only load the default gems and the gems for the current environment.

Bundler.setup(:default, Rails.env)

Load Path

The first problem that I noticed is that my development gems were available on the load path in a non-development irb console. Since the Faker gem is only in the development group, it shouldn’t be available.

:001 > $LOAD_PATH.grep(/faker/)
 => ["~/.rvm/gems/ruby-1.8@blog-bundler/gems/faker-1.1.2/lib"] 

It took me a while to figure out why this was happening. I learned that Bundler adds -r bundler/setup to the RUBYOPT environment variable. Rails starts its console by making an exec call to irb. The require of bundler/setup is equivalent to calling Bundler.setup without any argument, so Bundler adds all gems to the load path. It doesn’t actually require the gems, but I’d still prefer to not have development gems on the load path in production.

require "rubygems"
require "bundler"
Bundler.setup(:default)
p $LOAD_PATH.grep(/faker/)
#=> []
exec "irb"
:001 > $LOAD_PATH.grep(/faker/)
 => ["~/.rvm/gems/ruby-1.8@blog-bundler/gems/faker-1.1.2/lib"] 

Bundler::GemNotFound

The next problem that I ran into was that Bundler was raising an exception stating that gems needed to be installed in production even if they were only declared in the development group. Using the Gemfile from above and uninstalling thin, I’d get an error when calling Bundler.setup(:default).

# gems/bundler-1.2.0/lib/bundler/spec_set.rb:90:in `materialize':
# Could not find thin-1.5.0 in any of the sources (Bundler::GemNotFound)

Intuitively, I wouldn’t think that Bundler.setup(:default) (before the exec irb) would require development gems to be installed since they’re not actually used.

Bundle Install

After digging through the Bundler source code for a while, I figured out the problem. A side effect of running bundle install is that Bundler writes a .bundle/config file. If you run bundle install --without development, then the file looks like:

--- 
BUNDLE_WITHOUT: development

Setting BUNDLE_WITHOUT makes the irb exec and the Bundler.setup call work as expected. Development gems no longer get added to the load path inside irb, and bundler no longer requires development gems to be installed even though they’re not loaded. You can test this by adding this line to the script above:

ENV["BUNDLE_WITHOUT"] = "development"

I think this issue happens because Bundler expects bundle install to be run to install gems. Because I already had system gems installed and all other gems unpacked into the vendor/gems directory, I didn’t think that I needed to run it. More importantly, I specifcally didn’t want to run it in production yet because I wanted to roll this release out without modifying the Capistrano scripts. It’s unintuitive that Bundler.setup(:some_group) will require gems in other groups to be installed unless BUNDLE_WITHOUT is also set. Or, if Bundler does expect install to run before any calls to setup, then Bundler could raise an exception to communicate that. I’d like to submit a patch to make this more clear, although changes may cause issues with backwards compatibility. I’ll either work around this issue for now, or go ahead and update deployment scripts to run bundle install when deploying, though it should be unnecessary.

Rails 3.0.20 and 2.3.16

Posted 4 months back at The Ruby Show

In this episode, Jason and Peter talk about the latest security updates to Rails, Threads in Ruby, and the usual round up of interesting blog posts.

What happens when MiniTest runs, or, what I think about testing using classes

Posted 4 months back at interblah.net - Home

I think I can see the end of my Ruby Testing Quest in sight.

As one part of the final leg of this journey, I want to take a not-too-deep dive into how the some principle testing frameworks actually work, so that I can better clarify in my own mind what distinguishes them, and perhaps, if we are lucky, draw out some attributes that may help me. Somehow.

We’re going to start with MiniTest. We’ll also look at RSpec and Kintama, but not right now. This is already crazy-long.

A simple MiniTest example

Let’s say you have the following test case:

require "minitest/autorun"

class SomethingTest < MiniTest::Unit::TestCase
  def setup
    @something = Object.new
  end

  def test_something
    refute_nil @something
  end

  def teardown
    @something = nil
  end
end

This test is obviously extremely dull and pointless, but it contains just enough to exercise the major parts of MiniTest that I care about.

The two hallmark attributes here are:

  • creating an explicit subclass of a framework class (SomethingTest < MiniTest::Unit::TestCase)
  • defining test behaviour within explicit methods (def setup, def test_something and def teardown).

Running the test

The simplest way of running this test would be to save it in some file (something_test.rb) and run it from the command-line.

$ ruby something_test.rb
Run options: --seed 24486

# Running tests:

.

Finished tests in 0.000866s, 1154.7344 tests/s, 1154.7344 assertions/s.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

So – what’s actually happening here?

Autorun

The first line in the file (require "minitest/autorun"), when evaluated, loads the MiniTest library and then calls MiniTest::Unit.autorun, which installs an at_exit hook – a block of code that will be run when this Ruby interpreter process starts to exit.

Our command in the shell (ruby something_test.rb) tells Ruby to load the contents of something_test.rb, which after loading MiniTest simply defines a class with some methods, and nothing else, so after the definition of SomethingTest is finished Ruby starts to exit, and the at_exit code is invoked.

Within this block, a few things happen, but only a small part is particularly relevant to us at the moment: the method MiniTest::Unit.new.run is run, with the contents of ARGV from the command line (in this case an empty Array, so we’ll ignore them as we continue).

MiniTest::Unit, a.a. the “runner”

The call to MiniTest::Unit.new.run simply calls MiniTest::Unit.runner._run, passing the command-line arguments through. runner is a class method on MiniTest::Unit, which returns an instance of MiniTest::Unit by default, although it can be configured to return anything else by setting MiniTest::Unit.runner = <something else>.

So, an instance of MiniTest::Unit was created in the unit test, which then calls run on another newly-created instance of it. It’s mildly confusing, but I believe the purpose is to allow you to completely customise how the tests run by being able to use any object with a _run method. From here on, we’ll assume that the default runner (an instance of MiniTest::Unit) was used.

The default _run method parses the ARGV into arguments (which we’ll ignore right now since in our example they are empty) and then loops through the plugins (another modifiable property of MiniTest::Unit class), which is really just an array of strings which correspond to methods on the MiniTest::Unit “runner” instance. By default, this is all methods which match run_*, and unless you’ve loaded extensions to MiniTest, it is just run_tests:

$ MiniTest::Unit.plugins
# => ["run_tests"]

The run_tests method calls the _run_anything method with the argument :tests. Within _run_anything, the argument is used to select the set of “suites” by kind (“test” suites or “bench” suites, but basically the classes that contain your actual tests).

The actual set of “suites” is returned by calling TestCase.test_suites in this instance. So what does it return? Let’s take a diversion to see what’s going on there.

The test suites, a.k.a TestCase subclasses, a.k.a. your actual tests

Take another look at the content of our test file:

class SomethingTest < MiniTest::Unit::TestCase
  def setup
    @something = Object.new
  end

  def test_something
    refute_nil @something
  end

  def teardown
    @something = nil
  end
end

When we subclassed MiniTest::Unit::TestCase as SomethingTest, the inherited hook on the superclass is called by Ruby with SomethingTest as an argument.

This stashes a reference to the class SomethingTest in an class variable1. The TestCase.test_suites method that we were looking at above returns all those subclasses, sorted by name:

MiniTest::Unit::TestCase.test_suites
# => [SomethingTest]

Running a “suite”2

Back in the _run_anything method, those suites are passed to the _run_suites method, which maps them into their results by passing each to the _run_suite method.

The _run_suite method is responsible for selecting those tests within a suite (returned by the test_methods method on your TestCase subclass) which match any filters (i.e. -n /test_something/).

SomethingTest.test_methods
# => ["test_something"]

The default filter is /./, which will match everything that test_methods returns. For each matching method name, it instantiates a new instance of your suite class, with the method name as an argument to the intialiser, i.e. SomethingTest.new("test_something").

The run method is then called on that instance, with the runner (the instance of MiniTest::Unit that was returned by MiniTest::Unit.runner) as an argument. If you wanted to do the same in the console, it basically amounts to this:

runner = MiniTest::Unit.new
suite = SomethingTest.new("test_something")
suite.run(runner)
# => "."

An actual test running

We’re now at the point where the code from your test is significantly involved. Within the run method, the following methods are called3:

  • setup – this is the method defined in your TestCase subclass. In our example, this results in the instance variable @something being set:
      def setup
        @something = Object.new
      end
    
  • run_test, with the test name that passed to the initializer as an argument. This method is simply an alias for __send__, so the effect is that the method corresponding to your test name is invoked. In our case, the body of test_something runs:
      def test_something
        refute_nil @something
      end
    
  • runner.record – this passes information about the name of the test, how long it took and how many assertions were called back to the runner instance

If we reach this point in the method, it means that the test method returned without raising any exceptions, and so the test is recorded as a pass.

However, if an exception was raised – either by the test, or by a failing assertion – then the test is marked as a failure, and the exception is passed as an argument in a corresponding call to runner.record.

  • teardown – This method is run via an ensure block, so that it will be invoked whether or not an exception occured. In our example, the @something instance variable is set to nil:
      def teardown
        @something = nil
      end
    

Various other things happen, but this is the essential core of how MiniTest works: an instance of your TestCase subclass is created, and then the setup, test and teardown methods are invoked on it.

After the test has run

The run method returns a “result”, which is normally a character like . or F or E. This ultimately gets spat out to whatever is going to be doing the output (normally STDOUT). We saw this output above when we manually instantiated SomethingTest and then called run on it.

Actually, the puke method is called for anything other than a pass, which writes a more detailed string into a @report instance variable, and then returns the first character of that string (Skipped ...S, Failed ...F and so on).

Back up into MiniTest

Once run method finishes, the result is printed out, and the number of assertions stored on the instance is collected. The test method names that we were iterating over – the result of SomethingTest.test_methods above – are sequentially mapped into this number of assertions, and the final returned value of the _run_suite method is a two element array, the first being the number of tests and the second being the total number of assertions, for each test that ran. In our example, this would be [1,1] – one test and one assertion in total:

runner = MiniTest::Unit.new
runner._run_suite(SomethingTest, :test)
# => [1, 1]

Back up in the _run_suites method, each TestCase is being mapped via into this pair of numbers:

runner._run_suites([SomethingTest], :test)
# => [[1, 1]]

Back up one level further in the _run_anything method, those numbers are summed to return the total number of tests and the total number of assertions, across the whole run of test suites. Finally, these numbers are printed out, and then any failures that were gathered by the calls to runner.record when each test was running.

When the _run method itself finally finishes, taking us back into the at_exit block we started in, it returns the number of errors plus failures that were counted. This value doesn’t seem to be used, and disappears into the quantum foam of energy and information to which we all, ultimately, return.


Running tests within the console

We’ve actually seen already how we could start to poke around with tests without running them all. We can run a single test relatively easily, and determine whether or not it passed:

runner = MiniTest::Unit.new
suite = SomethingTest.new("test_something")
suite.run(runner)
# => "."

Unfortunately, there’s no simple way to run a group of tests (a “suite” or a “testcase” or what have you) aside from using the runner to specify a filter based on names. In other words, there’s no behaviour inherent within the TestCase class that lets you examine the result of the tests it contains. The information about which test failed, and why, leaves the instance when runner.report is called, and it’s only the runner that “knows” (in a very, very weak sense) about the state of more than just the test that is running now.

Instead the TestCase subclass is really just a collection of test_ methods along with the underlying behaviour to execute them (the run method that we examined above, and all of the supporting methods it invokes).

A test’s environment

One of the aspects of test frameworks that interests me most is what provides the environment for each test. What I mean by environment here are things like

  • the implicit value of self
  • how instance variables declared outside of the test relate to the code within the test
  • how methods defined outside the test relate to the code within the test

When a TestCase subclass is instantiated, that instance provides the environment for the test to execute. MiniTest, like test-unit before it, is using the familiar conceptual relationship between classes, objects and methods in Ruby to implicitly communicate that instance variables created or modified in one method, like setup, will be available within our tests, just like normal Ruby code.

This is, I believe, the main reason behind some of the preference towards MiniTest or test-unit style frameworks – they use “less magic”, they are “closer to the metal” – because they use the same conceptual relationships between methods, variables and self as we use when doing all other programming.

This may be so familiar as to seem obvious; methods can of course call methods within the same class, and instance variables set in one method (e.g. setup) can of course be accessed by other methods (e.g. test_something) within the same class. Therefore implementing test suites like this is surely only natural!

Yes, indeed. But doing so is not without consequence.

For example, it’s not typical behaviour to create a new instance of a class just to invoke a single method on it, but that happens for every test_ method. I hope you’ll agree that that seems far less natural. But this has to happen so that each test runs within a clean environment, without any of the changes the previous test might have made to the instance variables they both use, and without any trace of the instance variables previous tests may have created.

If your test framework has those hallmark attributes I mentioned above – a class definition to contain tests, and tests defined as methods – then creating a new instance of that class to run each individual test is an inevitable consequence, unless you want to do some incredible gymnastics behind the scenes.

Examining test environments

Before I climb the ivory tower at the end of this post, let’s have one final code interlude, using these test objects we are creating in the console.

I’ve often imaginged that it would be very useful if, when a test fails, you got a dump of all of the values of every instance value in that test. I don’t know about you, but I am very bored of peppering tests with puts statements, or trying to use logs to decipher what happened, when I know that if I could just see the instance variables then I could tell what was failing, and why.

How about this:

require "minitest/unit"

class AnotherTest < MiniTest::Unit::TestCase
  def setup
    @value = 123
  end

  def test_something
    assert_nil @value
  end
end

class MiniTest::Unit::TestCase
  def environment(hide_framework_variables = true)
    variables = instance_variables
    variables.reject! { |v| v.to_s =~ /^@(_|passed)/ } if hide_framework_variables
    variables.inject({}) do |h, v|
      h[v] = instance_variable_get(v)
      h
    end
  end
end

runner = MiniTest::Unit.new
test = AnotherTest.new("test_something")
test.run(runner)
# => "F"

We can see that the test failed, but now we can also look at the instance variables within that test:

test.environment
# => {:@value=>123}

In this test it’s pretty trivial, but maybe you can imagine that being useful when you have a ton of ActiveRecord objects flying around? Particularly if you also patch whatever is outputting your test results to print the contents of environment for all failing tests.

If you’re curious, you can also take a look at the other instance variables that MiniTest has created behind the scenes, mostly prefixed with _ to indicate an informal ‘privacy’:

test.environment(false)
# => {:@__name__=>"test_something", :@__io__=>nil, :@passed=>false, :@value=>123, :@_assertions=>1}

Perhaps this might be worth developing into something useful? Maybe. It’s very much related to the other ideas that I’ve had about Rerunning tests in Ruby.

Using classes for test cases?

So, here we are at the foot of my ivory tower.

There’s nothing wrong with implementing test suites like MiniTest does, but it’s interesting to understand the consequences, both in terms of the impact to the test implementer and the design choices that it forces on the framework implementer. This is obviously particularly try if you’re trying to understand the different ways that one could compose test suites.

Using classes and methods is one way, but it’s not the only way to produce blocks of code (indeed, blocks are another) to be run in some specific way.

If we choose Ruby’s existing class system as the mechanism for collecting test behaviour together, we are bound by the rules and limitations of that class system when trying to do anything slightly more out of the ordinary, like dynamically composing abstract behaviour specifications.

Of all languages I’ve used, Ruby is by far the most forgiving regarding this; you can get an amazing amount of mileage out of subclassing, and including modules, and using “class methods” to modify the definition of classes at run-time.

Ruby Testing Diaspora

It’s really a credit to Ruby that, even within the niche of the ecosystem that testing libraries represent, and even within that, the libraries that build on MiniTest or test-unit, so much richness exists. Things like shoulda or coulda or contest could not possibly exist without this flexibility.

But that doesn’t mean that there aren’t occasions where you hit a problem using things like inclusion or inheritance. This has been on my mind for a while.

Hmm

It’s my intuition that these test suites that we’re writing… well, they shouldn’t be classes. They don’t describe things that you can instantiate sensibly and that then have behaviour. They certainly don’t send messages to one another, like “proper objects” do. Classes are just convenient containers for these loosely-related essentially-procedural test bodies.

I believe that this intuition is what lies behind my interest in other test frameworks. From it springs all the ideas about composing or describing the systems under test in more dynamic or more natural ways.

In the next article, we’ll look at how RSpec works under the hood, and finally how Kintama does. Without having done the comparison yet, my guess is that they are very similar, but even within the alternate approach of block-defined tests there are many different paths you can take…

  1. For some reason this collection of classes is stored in a Hash, but it seems like the keys of the hash are the only aspect used, so I don’t understand why it isn’t an Array…

  2. …a.k.a. TestCase subclass, a.k.a. your actual tests. I’m not sure why the MiniTest code is riddled with references to ‘suites’, when the classes that it’s actually running are called TestCases. Perhaps it’s a compromise involving historic names of classes in test-unit?

  3. There are actually quite a few more methods called, but I’m ignoring hooks principally used by plugin authors.

Sort lines alphabetically in vim

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

Imagine you’re working in vim. You come across this code:

gem 'clearance', '1.0.0.rc4'
gem 'neat'
gem 'stripe'
gem 'pg'
gem 'thin'
gem 'rails', '3.2.11'
gem 'bourbon'
gem 'simple_form'
gem 'strong_parameters'

You want to sort the list alphabetically. You select the lines visually:

Shift + V

You invoke the sort function:

:sort

You rejoice:

gem 'bourbon'
gem 'clearance', '1.0.0.rc4'
gem 'jquery-rails'
gem 'neat'
gem 'pg'
gem 'rails', '3.2.11'
gem 'responders'
gem 'simple_form'
gem 'stripe'
gem 'strong_parameters'
gem 'thin'

You dig deeper:

:help sort

Episode #341 - February 1st, 2013

Posted 4 months back at Ruby5

Hold on to your butts! RubyGems got pwned. What else is going on this half of this week? Well, a new way to interrogate your arrays, some wise words about random numbers in Ruby, a multi-line memoization technique, asynchronous requests with Thin, and oh, by the way, your CSS is garbage.

Listen to this episode on Ruby5

This episode is sponsored by New Relic
New Relic is the all-in-one web performance analytics product. It lets you manage and monitor web application performance, from the browser down to the line of code. With Real User Monitoring, New Relic users can see browser response times by geographical location of the user, or by browser type.

RubyGems.org Hacked
RubyGems.org was hacked Wednesday morning. An exploit in the Psych YAML gem was used to post the site's config files publicly. Additionally, it was possible that some gem source code was compromised. So, the server was shut down, preventing deploys based on bundler. Heroku disabled its deploys as well. All gems are being audited against known good sources.

queryable_array gem
From the readme:

A QueryableArray inherits from array and is intended to store a group of objects which share the same attributes allowing them to be searched.

Ruby and Random
What you don't know about random numbers in Ruby could hurt you! Jan Lelis shows us how and guides us toward the light that is SecureRandom.

Sexy Memoization
You're multi-line memoization methods make you look weak. Let Jack Danger show you how to sweeten them up.

Async request with Thin
Use Thin to make your longer running requests async.

CSS Trashman
CSS Trashman, because your CSS is garbage.

Salaries and Raises

Posted 4 months back at Dan Manges

A company’s approach to determining employee salary is often esoteric. Except in the case of a government job where salaries are required to be published, or one of those occasional startups that decides to be ultra-transparent, employees only have data on their own salary, and it’s often not clear how it’s determined. When I was the CTO of Braintree, I incorporated many of the ideas from Netflix to determine salaries. (Note: I am no longer involved in compensation discussions at Braintree, and the opinions here are mine, not Braintree’s.)

Determining Salary

I’ve never understood when an employee talks to his or her employer about quitting, and the employer includes a generous raise as part of a counter-proposal to stay. If the person was providing enough value to the company to deserve that compensation, and the company was willing to pay it, why weren’t they? Cynically, many companies have financial interest in keeping salaries in a lower percentile. Sadly, this seems equivalent to an employee trying to work the least amount possible while keeping their job. It’s likely not the type of relationship or culture that most companies want to have with their employees.

One of the questions that Netflix proposed helps guide salaries along these lines.

What would we pay to keep that person if they had a bigger offer elsewhere?

I tried to do this at Braintree. My goal was to never have a person ask for more money and for me to say “yes.” If people are compensated commensurate with the value they provide to the company, then there’s no need to negotiate.

I applied a similar thought process to job offers. It’s commonplace for employers to offer a salary slightly lower than they’re willing to pay, a job candidate to counter with a number that’s higher than they actually need to accept the job, and to go back-and-forth a bit before settling on a number in the middle. This negotiation process is inane, especially when it’s become the norm to the point of being predictable. Instead, I’d try to give employees the best possible offer initially. It’s sometimes difficult to determine this for new employees because their value is unproven, but it should be possible based on salary history, role, and experience.

Determining Raises

When determining raises, I’d apply the same strategy used to determine salary.

What would we pay to keep that person if they had a bigger offer elsewhere?

Offering the best possible raise helps reduce company politics. People should need to perform to earn raises, not be an expert negotiator. Again, a counter-offer from another company should rarely, if ever, prompt a raise.

In general, raises will likely be bigger the first few years that an employee is with a company than in later years. At most companies, historical context greatly increases the value that somebody provides to the company. The difference between somebody with 1 year of experience with the company and somebody with 2 years is usually large. The difference between 7 and 8 years of experience with the company is smaller. However, sometimes an initial job offer assumes the initial ramp up period, so this may not precisely be the case. General market conditions are also a factor.

If an employee was brought in at a lower salary than they currently deserve, then adjust them to where they should be. If an employee was brought in on the higher end, let them know where they’re at and set realistic expectations around opportunities for growth.

A note on actually executing the raise: don’t communicate it in terms of percentages. I’ve never heard someone say to me “if only I made 8% more per month, I could buy a new car.” I have heard people say things like “if I made another $500 per month, I could buy a new car.” People don’t generally think of their income in terms of percentages; communicate it in dollars. A percentage-based raise is likely using the wrong approach anyway, unless you calculated the percentage of increase in value provided to the company from the previous year. Raises should be more of a new job offer, based on the value that the person provides to the company after having been there a certain period of time, than a delta calculation from the previous year based on nebulous math.

At Netflix, market comp always applies: Essentially, top of market comp is re-established each year for high performing employees

Focus on value and pay employees accordingly. Invest in people, and you’ll build a team that is energetically engaged every day in making the company, culture, services, and support, exceptional.

Asking Customers for Feedback on Product Ideas

Posted 4 months back at Dan Manges

You have an idea for a new product feature to build, and you think your customers will love it. Before you start to build it, you want to get some feedback on the idea. You contact some of your customers who will be interested in the new feature and you ask them what they think. Your customers say that they love it, and you go back to your team, excited to start building.

That process is commonly used, but it’s flawed. Certainly, soliciting feedback is much better than building a new feature based only on conjecture. But to get good feedback from your customers, you can’t simply ask them what they think. To get valuable feedback, you need to include a form of consideration in the question; otherwise, your customers don’t have any reason to say “no.”

Pricing

Jason Fried recently wrote about how to price something. He made a similar point.

You can’t ask people who haven’t paid how much they’re willing to pay. Their answers don’t matter because there’s no cost to saying “yes” ”$20” “no” ”$100”. They all cost the same – nothing.

Instead of asking, Jason recommends actually charging.

So put a price on it and put it up for sale. If people buy that’s a yes.

If your new product idea is something that you plan to charge for, you could ask your customers to pay for it ahead of building it. However, many product features are meant to enhance the value of a current offering, not to provide an additional revenue stream. You need another way to find out if your customers are truly interested, or if they’re only saying they are because there’s no cost to saying “yes.”

Relative Importance

One approach that results in better feedback is to ask your customers about features relative to each other, rather than asking about the value in isolation. This is similar to conjoint analysis in marketing. Take between 5 and 10 of the top ideas that you have to enhance your product and ask your customers to rank them in the order that they’d most like to have the features. After collecting responses, you can apply an algorithm such as instant-runoff voting to determine the top choice. Following this process introduces consideration. It’s no longer a question of if the customer wants the feature (why wouldn’t they if it’s free?), it’s a question of if the customer wants a feature more than another. In other words, getting the feature will cost something (namely, other features that they could have had instead).

Of course, you can’t always determine the value of features by asking your customers directly. Sometimes you need to address unstated or future needs. If you do want to use customer feedback as support for initiating a project, do it with relative importance.

High Performance Networking in Google Chrome

Posted 4 months back at igvita.com

The following is a draft chapter for the upcoming "The Performance of Open Source Applications" (POSA), a sequel to The Architecture of Open Source Applications. POSA is a collection of essays about performance optimization, designing for performance, managing performance as part of a development process, and more. The book will be published in Spring 2013 under a Creative Commons license with royalties going to Amnesty International.


History and guiding principles of Google Chrome #

Google Chrome was first released in the second half of 2008, as a beta version for the Windows platform. The Google-authored code powering Chrome was also made available under a permissive BSD license - aka, the Chromium project. To many observers, this turn of events came as a surprise: the return of the browser wars? Could Google really do much better?

"It was so good that it essentially forced me to change my mind..." - Eric Schmidt, on his initial reluctance to the idea of developing Google Chrome.

Turns out, they could. Today Chrome is one of the most widely used browsers on the web (35%+ of the market share according to StatCounter) and is now available on Windows, Linux, OS X, Chrome OS, as well as Android and iOS platforms. Clearly, the features and the functionality resonated with the users, and many innovations of Chrome have also found their way into other popular browsers.

The original 38-page comic book explanation of the ideas and innovations of Google Chrome offers a great overview of the thinking and design process behind the popular browser. However, this was only the beginning. The core principles that motivated the original development of the browser continue to be the guiding principles for ongoing improvements in Chrome:

  • Speed: the objective is to make the fastest browser
  • Security: provide the most secure environment to the user
  • Stability: provide a resilient and stable web application platform
  • Simplicity: sophisticated technology, wrapped in a simple user experience

As the team observed, many of the sites we use today aren't just web pages, they are applications. In turn, the ever more ambitious applications require speed, security, and stability. Each of these deserves its own dedicated chapter, but since our subject is performance, our focus will be primarily on speed.

The many facets of performance #

A modern browser is a platform, just like your operating system, and Google Chrome is designed as such. Prior to Google Chrome, all browsers were built as a monolithic, single process applications. All open pages shared the same address space and contended for the same resources. A bug in any page, or the browser, ran the risk of compromising the entire experience.

By contrast, Chrome works on a multi-process model, which provides process and memory isolation, and a tight security sandbox for each tab. In an increasingly multi-core world, the ability to isolate the processes as well as shield each open tab from other misbehaving pages has by itself proven to give Chrome a significant performance edge over the competition. In fact, it is important to note that most other browsers have followed suit, or are in the process of migrating to similar architecture.

With an allocated process in place, the execution of a web program primarily involves three tasks: fetching resources, page layout and rendering, and JavaScript execution. The rendering and script steps follow a single-threaded, interleaved model of execution - it is not possible to perform concurrent modifications of the resulting Document Object Model (DOM). This is in part due to the fact that JavaScript itself is a single threaded language. Hence, optimizing how the rendering and script execution runtimes work together is of critical importance, both to the web developers building the applications as well as the developers working on the browser.

For rendering, Chrome uses WebKit, which is a fast, open-source, and standards compliant layout engine. For JavaScript, Chrome ships with its own, heavily optimized "V8" JavaScript runtime, which was also released as a standalone open-source project and has found its way into many other popular projects - e.g., runtime for node.js. However, optimizing V8 JavaScript execution, or the WebKit parsing and rendering pipelines won't do much good if the browser is blocked on the network, waiting for the resources to arrive!

The ability of the browser to optimize the order, priority, and latency of each network resource is one of the most critical contributors to the overall user experience. You may not be aware of it, but Chrome's network stack is, quite literally, getting smarter every day, trying to hide or decrease the latency cost of each resource: it learns likely DNS lookups, it remembers the topology of the web, it preconnects to likely destination targets, and more. From the outside, it presents itself as a simple resource fetching mechanism, but from the inside it is an elaborate and a fascinating case study for how to optimize web performance and deliver the best experience to the user.

Let's dive in...

What is a modern web application? #

Before we get to the tactical details of how to optimize our interaction with the network, it helps to understand the trends and the landscape of the problem we are up against. In other words, what does a modern web page, or application look like?

The HTTP Archive project tracks how the web is built, and it can help us answer this question. Instead of crawling the web for the content, it periodically crawls the most popular sites to record and aggregate analytics on the number of used resources, content types, headers, and other metadata for each individual destination. The stats, as of January 2013, may surprise you. An average page, amongst the top 300,000 destinations on the web is:

  • 1280 KB in size
  • composed of 88 resources
  • connects to 30+ distinct hosts

Let that sink in. Over 1MB in size on average, composed of 88 resources such as images, JavaScript, and CSS, and delivered from 30 different own and third-party hosts! Further, each of these numbers have been steadily increasing over the past few years, and there are no signs of stopping. We are increasingly building larger and more ambitious web applications.

Applying basic math to the HTTP Archive numbers reveals that an average resource is about 12KB in size (1045 KB / 84 resources), which means that most network transfers in the browser are short and bursty. This presents its own set of complications because the underlying transport (TCP) is optimized for large, streaming downloads. Let's peel back the onion and inspect one of these network requests...

The life of a resource request on the wire #

The W3C Navigation Timing specification provides a browser API and visibility into the timing and performance data behind the life of every request in the browser. Let's inspect the components, as each is a critical piece of delivering the optimal user experience:

Given the URL of a resource on the web, the browser starts by checking its local and application caches. If you have previously fetched the resource and the appropriate cache headers were provided (Expires, Cache-Control, etc.), then it is possible that we may be allowed to use the local copy to fulfill the request - the fastest request is a request not made. Alternatively, if we have to revalidate the resource, if it expired, or if we simply haven't seen it before, then a costly network request must be dispatched.

Given a hostname and resource path, Chrome first checks for existing open connections it is allowed to reuse - sockets are pooled by {scheme, host, port}. Alternatively, if you have configured a proxy, or specified a proxy auto-config (PAC) script, then Chrome checks for connections through the appropriate proxy. PAC scripts allow for different proxies based on URL, or other specified rules, each of which can have its own socket pool. Finally, if neither of the above conditions is matched, then the request must begin by resolving the hostname to its IP address - aka, a DNS lookup.

If we are lucky, the hostname may already be cached in which case the response is usually just one quick system call away. If not, then a DNS query must be dispatched before any other work can happen. The time taken to do the DNS lookup will vary based on your internet provider, the popularity of the site and the likelihood of the hostname to be in intermediate caches, as well as the response time of the authoritative servers for that domain. In other words, there are a lot of variables at play, but it's not unusual for a DNS lookup to take up to several hundred milliseconds - ouch.

With the resolved IP address in hand, Chrome can now open a new TCP connection to the destination, which means that we must perform the "three-way handshake": SYN > SYN-ACK > ACK. This exchange adds a full roundtrip of latency delay to each and every new TCP connection - no shortcuts. Depending on the distance between the client and the server, as well as the chosen routing path, this can yield from tens to hundreds, or even thousands, of milliseconds of delay. All of this work and latency is before even a single byte of application data has hit the wire!

Once the TCP handshake is complete, and if we're connecting to a secure destination (HTTPS), then the SSL handshake must take place. This can add up to two additional roundtrips of latency delay between client and server. If the SSL session is cached, then we can "escape" with just one additional roundtrip.

Finally, Chrome is able to dispatch the HTTP request (requestStart in the Nav Timing figure above). Once received, the server can process the request and then stream the response data back to the client. This incurs a minimum of a network roundtrip, plus the processing time on the server. Following that, we're done. Well, that is unless the actual response is an HTTP redirect! In which case, we may have to repeat the entire cycle once over. Have a few gratuitous redirects on your pages? You may want to revisit that decision!

Have you been counting all the delays? To illustrate the problem, let's assume the worst case scenario for a typical broadband connection: local cache miss, followed by a relatively fast DNS lookup (50 ms), TCP handshake, SSL negotiation, and a relatively fast (100 ms) server response time, with a round-trip time of 80 ms (an average round-trip across continental USA):

  • 50ms for DNS
  • 80ms for TCP handshake (one RTT)
  • 160ms for SSL handshake (two RTT's)
  • 40ms for request to server
  • 100ms for server processing
  • 40ms for response from the server

That's 470 milliseconds for a single request, which translates to over 80% of network latency overhead as compared to the actual server processing time to fulfill the request - we have some work to do here! In fact, even 470 milliseconds may be an optimistic estimate:

  • If the server response does not fit into the initial TCP congestion window (4-15 KB), then one or more additional roundtrips of latency is introduced
  • SSL delays could get even worse if we need to fetch a missing certificate or perform an online certificate status check (OCSP), both of which will require an entirely new TCP connection, which can add hundreds and even thousands of milliseconds of additional latency

What is "fast enough"? #

The network overhead of DNS, handshakes, and the roundtrip times is what dominates the total time in our earlier case - the server response time accounts for only 20% of the total latency! But, in the grand scheme of things, do these delays even matter? If you are reading this, then you probably already know the answer: yes, very much so.

Past user experience research paints a consistent picture in what we, as users, expect in terms of responsiveness of any applications, both offline and online:

Delay User Reaction
0 - 100ms Instant
100 - 300ms Small perceptible delay
300 - 1000ms Machine is working
1s+ Mental context switch
10s+ I'll come back later...

The table above also explains the unofficial rule of thumb in the web performance community: render your pages, or at the very least, provide visual feedback in under 250 ms to keep the user engaged. This is not speed simply for speed's sake. Studies at Google, Amazon, Microsoft, as well as thousands of other sites show that additional latency has a direct impact on the bottom line of your site: faster sites yield more pageviews, higher engagement from the users, and see higher conversion rates.

So, there you have it, our optimal latency budget is 250 ms, and yet as we saw in the example above, the combination of a DNS lookup, the TCP and SSL handshakes, and propagation times for the request add up to 370 ms. We're 50% over budget, and we still haven't factored in the server processing time!

To most users and even web-developers, the DNS, TCP, and SSL delays are entirely transparent and are negotiated at network layers to which few of us descend or think about. And yet, each of these steps is critical to the overall user experience, since each extra network request can add tens or hundreds of milliseconds of latency. This is the reason why Chrome's network stack is much, much more than a simple socket handler.

Now that we've identified the problem, let's dive into the implementation details...

Chrome's network stack from 10,000 feet #

Multi-process architecture #

Chrome's multi-process architecture carries important implications for how each network request is handled within the browser. Under the hood, Chrome actually supports four different execution models that determine how the process allocation is performed.

By default, desktop Chrome browsers use the process-per-site model, that isolates different sites from each other, but groups all instances of the same site into the same process. However, to keep things simple, let's assume one of the simplest cases: one distinct process for each open tab. From the network performance perspective, the differences here are not substantial, but the process-per-tab model is much easier to understand.

The architecture dedicates one render process to each tab, which itself contains an instance of the WebKit open-source layout engine for interpreting and layout out the HTML (aka, "HTML Renderer" in the diagram), an instance of the V8 JavaScript engine, and the glue code to bridge these and a few other components. If you are curious, the Chromium wiki contains a great introduction to the plumbing.

Each of these "render" processes is executed within a sandboxed environment that has limited access to the user's computer - including the network. To gain access to these resources, each render process communicates with the browser (kernel) process, which is able to impose security and access policies on each renderer.

Inter-process communication (IPC) and Multi-process resource loading #

All communication between the renderer and the kernel process in Chrome is done via IPC. On Linux and OSX, a socketpair() is used, which provides a named pipe transport for asynchronous communication. Each message from the renderer is serialized and passed to a dedicated I/O thread, which dispatches it to the kernel process. On the receiving end, the kernel process provides a filter interface, which allows Chrome to intercept resource IPC requests (see ResourceMessageFilter) which should be handled by the network stack.

One of the advantages of this architecture is that all resource requests are handled entirely on the I/O threads and neither any UI generated activity, or network events interfere with each other. The resource filter runs in the I/O thread in the browser process, intercepts the resource request messages, and forwards them to a ResourceDispatcherHost singleton in the browser process.

The singleton interface allows the browser to control each renderer's access to the network, but it also enables efficient, and consistent resource sharing:

  • Socket pool and connection limits: the browser is able to enforce limits on the number of open sockets per profile (256), proxy (32), and {scheme, host, port} (6) groups. Note that this allows up to six HTTP and six HTTPS connections to the same {host, port}!
  • Socket reuse: persistent TCP connections are retained in the socket pool for some time after servicing the request to enable connection reuse, which avoids the extra DNS, TCP, and SSL (if required) setup overhead imposed on each new connection.
  • Socket late-binding: requests are associated with an underlying TCP connection only once the socket is ready to dispatch the application request, allowing better request prioritization (e.g., arrival of a higher priority request while the socket was connecting), better throughput (e.g., re-use of a "warm" TCP connection in cases where an existing socket becomes available while a new connection is being opened), as well as a general-purpose mechanism for TCP pre-connect, and a number of other optimizations.
  • Consistent session state: authentication, cookies, and cached data is shared between all render processes.
  • Global resource and network optimizations: the browser is able to make decisions across all render processes and outstanding requests. For example, giving network priority to the requests initiated by the foreground tab.
  • Predictive optimizations: by observing all network traffic, Chrome is able to build and refine predictive models to improve performance.
  • ... and the list goes on.

As far as the render process is concerned, it is simply sending a resource request message over IPC, which is tagged with a unique request ID to the browser process, and the browser kernel process handles the rest.

Cross-platform resource fetching #

One of the chief concerns in the implementation of Chrome's network stack is portability across many different platforms: Linux, Windows, OS X, Chrome OS, Android, and iOS. To address this challenge, the network stack is implemented as a mostly single-threaded (there are separate cache and proxy threads), cross-platform library, which allows Chrome to reuse the same infrastructure and provide the same performance optimizations, as well as a greater opportunity for optimization across all platforms.

All of the network code is, of course, open source and can be found in the "src/net" subdirectory. We won't examine each component in detail, but the layout of the code itself tells you a lot about its capabilities and structure. A few examples:

net/android Bindings to the Android runtime
net/base Common net utilities, such as host resolution, cookies, network change detection, and SSL certificate management
net/cookies Implementation of storage, management, and retrieval of HTTP cookies
net/disk_cache Disk and memory cache implementation for web resources
net/dns Implementation of an asynchronous DNS resolver
net/http HTTP protocol implementation
net/proxy Proxy (SOCKS and HTTP) configuration, resolution, script fetching, ...
net/socket Cross-platform implementations of TCP sockets, SSL streams, and socket pools
net/spdy SPDY protocol implementation
net/url_request URLRequest, URLRequestContext, and URLRequestJob implementations
net/websockets WebSockets protocol implementation

Each of the above makes for a great read for the curious - the code is well documented, and you'll find plenty of unit tests for every component.

Architecture and performance on mobile platforms #

Mobile browser usage is growing at an exponential rate and even by modest projections, it will eclipse desktop browsing in the not so distant future. Needless to say, delivering an optimized mobile experience has been a top priority for the Chrome team. In early 2012, Chrome for Android was announced, and a few months later, Chrome for iOS followed.

The first thing to note about the mobile version of Chrome, is that it's not simply a direct adaptation of the desktop browser - that would not deliver the best user experience. By its very nature, the mobile environment is both much more resource constrained, and has many fundamentally different operating parameters:

  • Desktop users navigate with the mouse, may have overlapping windows, have a large screen, are mostly not power constrained, usually have a much more stable network connection, and have access to much larger pools of storage and memory.
  • Mobile users use touch and gesture navigation, have a much smaller screen, are battery and power constrained, are often on metered connections, and have limited local storage and memory.

Further, there is no such thing as a "typical mobile device". Instead there is a wide range of devices with varying hardware capabilities, and to deliver the best performance, Chrome must to adapt to the operating constraints of each and every device. Thankfully, the various execution models allow Chrome to do exactly that!

On Android devices, Chrome leverages the same multi-process architecture as the desktop version - there is a browser process, and one or more renderer processes. The one difference is that due to memory constraints of the mobile device, Chrome may not be able to run a dedicated renderer for each open tab. Instead, Chrome determines the optimal number of renderer processes based on available memory, and other constraints of the device, and shares the renderer process between the multiple tabs.

In cases where only minimal resources are available, or if Chrome is unable to run multiple processes, it can also switch to use a single-process, multi-threaded processing model. In fact, on iOS devices, due to sandboxing restrictions of the underlying platform, it does exactly that - it runs a single, but multi-threaded process.

What about network performance? First off, Chrome uses the same network stack on Android and iOS, as it does on all other versions. This enables all of the same network optimizations across all platforms, which gives Chrome a significant performance advantage. However, what is different, and is often adjusted based on the capabilities of the device and the network in use, are variables such as priority of speculative optimization techniques, socket timeouts and management logic, cache sizes, and more.

For example, to preserve battery, mobile Chrome may opt-in to use lazy closing of idle sockets - sockets are closed only when opening new ones to minimize radio use. Similarly, since prerendering (which we will discuss below), may require significant network and processing resources, it is often only enabled when the user is on Wi-Fi.

Optimizing the mobile browsing experience is one of the highest priority items for the Chrome development team, and we can expect to see a lot of new improvements in the months and years to come. In fact, it is a topic that deserves its own separate chapter - perhaps in the next installment of the POSA series!

Speculative optimization with Chrome's Predictor #

Chrome gets faster as you use it. This feat is accomplished with the help of a singleton Predictor object, which is instantiated within the browser kernel process, and whose sole responsibility is to observe network patterns and to learn and anticipate likely user actions in the future. A few example signals processed by the Predictor include:

  • Users hovering their mouse over a link is a good indicator of a likely, upcoming navigation event, which Chrome can help accelerate by dispatching a speculative DNS lookup of the target hostname, as well as potentially starting the TCP handshake. By the time the user clicks, which takes ~200 ms on average, there is a good chance that we have already completed the DNS and TCP steps, allowing us to eliminate hundreds of milliseconds of extra latency for the navigation event.
  • Typing in the Omnibox (URL) bar triggers high-likelihood suggestions, which may similarly kick off a DNS lookup, TCP pre-connect, and can even pre-render the page in a hidden tab!
  • Each one of us has a list of favorite sites that we visit every day. Chrome can learn the subresources on these sites and speculatively pre-resolve and perhaps even pre-fetch them to accelerate the browsing experience. And the list goes on...

Chrome learns the topology of the web, as well as your own browsing patterns, as you use it. If it does the job well, it can eliminate hundreds of milliseconds of latency from each navigation and get the user closer to the holy grail of the "instant page load". To achieve this goal, Chrome leverages four core optimization techniques:

DNS pre-resolve resolve hostnames ahead of time, to avoid DNS latency
TCP pre-connect connect to destination server ahead of time, to avoid TCP handshake latency
Resource prefetching fetch critical resources on the page ahead of time, to accelerate rendering of the page
Page prerendering fetch the entire page with all of its resources ahead of time, to enable instant navigation when triggered by the user

Each decision to invoke one or several of these techniques is optimized against a large number of constraints. After all, each is a speculative optimization, which means that if done poorly, it might trigger unnecessary work and network traffic, or even worse, have a negative effect on the loading time for an actual navigation triggered by the user.

How does Chrome address this problem? The predictor consumes as many signals as it can, which include user generated actions, historical browsing data, as well as signals from the renderer and the network stack itself.

Not unlike the ResourceDispatcherHost, which is responsible for coordinating all of the network activity within Chrome, the Predictor object creates a number of filters on user and network generated activity within Chrome:

  • IPC channel filter to monitor for signals from the render processes
  • ConnectInterceptor object is added to each request, such that it can observe the traffic patterns and record success metrics for each request

As a hands on example, the render process can trigger a message to the browser process with any of the following hints, which are conveniently defined in ResolutionMotivation (url_info.h):

enum ResolutionMotivation {
  MOUSE_OVER_MOTIVATED,     // Mouse-over initiated by the user.
  OMNIBOX_MOTIVATED,        // Omni-box suggested resolving this.
  STARTUP_LIST_MOTIVATED,   // This resource is on the top 10 startup list.
  EARLY_LOAD_MOTIVATED,     // In some cases we use the prefetcher to warm up
                            // the connection in advance of issuing the real
                            // request.

  // The following involve predictive prefetching, triggered by a navigation.
  // The referrinrg_url_ is also set when these are used.
  STATIC_REFERAL_MOTIVATED,  // External database suggested this resolution.
  LEARNED_REFERAL_MOTIVATED, // Prior navigation taught us this resolution.
  SELF_REFERAL_MOTIVATED,    // Guess about need for a second connection.

  // <snip> ...
};

Given such a signal, the goal of the predictor is to evaluate the likelihood of its success, and then to trigger the activity if resources are available. Every hint may have a likelihood of success, a priority, and an expiration timestamp, the combination of which can be used to maintain an internal priority queue of speculative optimizations. Finally, for every dispatched request from within this queue, the predictor is also able to track its success rate, which allows it to further optimize its future decisions.

Chrome network architecture in a nutshell #

  • Chrome uses a multi-process architecture, which isolates render processes from the browser process
  • Chrome maintains a single instance of the resource dispatcher, which is shared across all render processes, and runs within the browser kernel process
  • The network stack is a cross-platform, (mostly) single-threaded library
  • The network stack uses non-blocking operations to manage all network operations
  • Shared network stack allows efficient resource prioritization, reuse, and provides the browser with ability to perform global optimization across all running processes
  • Each render process communicates with the resource dispatcher via IPC
  • Resource dispatcher intercepts resource requests via a custom IPC filter
  • Predictor intercepts resources request and response traffic to learn and optimize future network requests
  • Predictor may speculatively schedule DNS, TCP, and even resource requests based on learned traffic patterns, saving hundreds of milliseconds when the navigation is triggered by the user

Lifetime of your browser session... #

With the 10,000 foot architecture view of the Chrome network stack in mind, let's now take a closer look at the kinds of user-facing optimizations enabled within the browser. Specifically, let's imagine we have just created a new Chrome profile and are ready to start our day.

Optimizing the cold-boot experience #

The first time you load your browser, it of course knows little about your favorite sites or navigation patterns. But, as it turns out, many of us follow the same routine after a cold-boot of the browser, where we may navigate to our email inbox, favorite news site, a social site, an internal portal, and so on. The specific sites will, of course, vary, but the similarity of all these sessions allows the Chrome predictor to accelerate your cold-boot experience!

Chrome remembers the top ten likely hostnames accessed by the user following the browser start - note that this is not the top ten global destinations, but specifically the destinations following a fresh browser start. As the browser loads, Chrome can trigger a DNS pre-fetch for the likely destinations! If you are curious, you can inspect your own startup hostname list by opening a new tab and navigating to chrome://dns. At the top of the page, you will find the list of the top ten likely startup candidates for your profile.

Above screenshot is an example from my own Chrome profile. How do I usually begin my browsing? Frequently by navigating to Google Docs if I'm working on an article such at this one. Not surprisingly, we see a lot of Google hostnames in the list!

Optimizing interactions with the Omnibox #

One of the innovations of Chrome was the introduction of the Omnibox, which unlike its predecessors handles much more than just destination URLs. Besides remembering the URLs of pages that the user visited in the past, it also offers full text search over your history (tip: instead of the URL, try typing the name of the page you've recently visited), as well as a tight integration with the search engine of your choice.

As the user types, the Omnibox automatically proposes an action, which is either a URL based on your navigation history, or a search query. Under the hood, each proposed action is scored with respect to the query, as well as its past performance. In fact, Chrome allows us to inspect this data by visiting chrome://predictors.

Chrome maintains a history of the user entered prefixes, the actions it has proposed, as well as the hit rate for each one. For my own profile, you can see that whenever I enter "g" in the Omnibox, there is a 76% chance that I'm heading to Gmail. Once I add an "m" (for "gm"), then the confidence rises to 99.8% - in fact, out of the 412 recorded visits, I didn't end up going to Gmail, after entering "gm" only once!

But, you're thinking, what does this have to do with the network stack? Well, the yellow and green colors for the likely candidates are also important signals for the ResourceDispatcher! If we have a likely candidate (yellow), Chrome may trigger a DNS pre-fetch for the target host. If we have a high confidence candidate (green), then Chrome may also trigger a TCP pre-connect once the hostname has been resolved. And finally, if both complete while the user is still deliberating, then Chrome may even pre-render the entire page in a hidden tab.

Alternatively, if there is no good match for the entered prefix based on past navigation history, then Chrome may issue a DNS pre-fetch and TCP pre-connect to your search provider, in anticipation of a likely search request.

An average user takes hundreds of milliseconds to fill in their query and to evaluate the proposed autocomplete suggestions. In the background, Chrome is able to pre-fetch, pre-connect, and in certain cases even pre-render the page, such that by the time the user is ready to hit the "enter" key, much of the network latency has already been eliminated!

Optimizing cache performance #

The best, and the fastest request, is a request not made. Whenever we talk about performance, we would be amiss if we didn't talk about the cache -- you are providing Expires, ETag, Last-Modified, and Cache-Control response headers for all the resources on your pages, right? If not, stop, go fix it, we'll wait.

Chrome has two different implementations of the internal cache: one backed by local disk, and second which stores everything in memory. The in-memory implementation is used for the Incognito browsing mode and is wiped clean whenever you close the window. Both implement the same internal interface (disk_cache::Backend, and disk_cache::Entry), which greatly simplifies the architecture - and if you are so inclined, allows you to easily experiment with your own, experimental cache implementations.

Internally, the disk cache implements its own set of data structures, all of which are stored within a single cache folder for your profile. Inside this folder, there are index files, which are memmapped when the browser starts, and data files which store the actual data, alongside the HTTP headers and other bookkeeping information. As an interesting sidenote, resources up to 16KB in size are stored in shared data block-files, and larger files get their own dedicated files on disk. Finally, for eviction, the disk cache maintains an LRU which uses ranking metrics such as frequency of access and age of resource into account.

If you are ever curious about the state of the Chrome cache, open a new tab and navigate to chrome://net-internals/#httpCache. Alternatively, if you want to see the actual HTTP metadata and the cached response, you can also visit chrome://cache, which will enumerate all of the resources currently available in the cache. From that page, search for a resource you're looking for and click on the URL to see the exact, cached headers and response bytes.

Optimizing DNS with prefetching #

We have already mentioned DNS pre-resolution on several occasions, so before we dive into the implementation, let's review the cases in which it may be triggered, and why:

  • The WebKit document parser, which runs in the render process, may provide a list of hostnames for all the links on the current page, which Chrome may, or may not choose to pre-resolve ahead of time.
  • Renderer process may trigger a mouse hover or "button down" event as an early signal of user's intent to perform a navigation.
  • The Omnibox may trigger a resolve request based on a high likelihood suggestion.
  • Chrome predictor may request hostname resolution based on past navigation and resource request data - more on this below.
  • The owner of the page may explicitly indicate to Chrome which hostnames it should pre-resolve.

In all cases, DNS pre-resolution is treated as a hint. Chrome does not guarantee that the pre-resolution will occur, rather it uses each signal in combination with its own predictor to assess the hint and decide on a course of action. In the "worst case", if we weren't able to pre-resolve the hostname in time, the user would have to wait for an explicit DNS resolution, followed by TCP connection time, and finally the actual resource fetch. However, when this occurs, the predictor can take note and adjust its future decisions accordingly - it gets faster, and smarter, as you use it.

One of the optimizations we have not covered previously is the ability of Chrome to learn the topology of each site and then use this information to accelerate future visits. Specifically, recall that an average page consists of 88 resources, which are delivered from 30+ distinct hosts. Well, each time you perform a navigation, Chrome may record the hostnames for the popular resources on the page, and during a future visit, it may choose to trigger a DNS pre-resolve and even a TCP pre-connect for some, or all of them!

To inspect the subresource hostnames stored by Chrome, navigate to chrome://dns and search for any popular destination hostname for your profile. In the example above, you can see the eight subresource hostnames that Chrome remembered for Google+, as well as stats for the number of cases when a DNS pre-resolution was triggered, or a TCP pre-connect was performed, as well as an expected number of requests that will be served by each. This internal accounting is what enables the Chrome predictor to perform its optimizations.

In addition to all of the internal signals, the owner of the site is also able to embed additional markup on their pages to request the browser to pre-resolve a hostname:

  <link rel="dns-prefetch" href="//host_name_to_prefetch.com">

Why not simply rely on the automated machinery in the browser? In some cases, you may want to pre-resolve a hostname which is not mentioned anywhere on the page. The canonical example is, of course, redirects: a link may point to a host, like an analytics tracking service, which then redirects the user to the actual destination. By itself, Chrome cannot infer this pattern, but you can help it by providing a manual hint and get the browser to resolve the hostname of the actual destination ahead of time.

So, how is this all implemented under the hood? The answer to this question, just like all other optimizations we have covered, depends on the version of Chrome, since the team is always experimenting with new and better ways to improve performance. However, broadly speaking, the DNS infrastructure within Chrome has two major implementations: historically, Chrome has relied on the platform-independent getaddrinfo() system call, and delegated the actual responsibility for the lookups to the operating system, however this approach is in the process of being replaced with Chrome's own implementation of an asynchronous DNS resolver.

The original implementation, which relied on the operating system, has its benefits: less and simpler code, and the ability to leverage the operating system's DNS cache. However, getaddrinfo() is also a blocking system call, which meant that Chrome had to create and maintain a dedicated worker thread-pool to allow it to perform multiple lookups in parallel. This unjoined pool was capped at six worker threads, which is an empirical number based on lowest common denominator of hardware - turns out, higher numbers of parallel requests can overload some users' routers!

For pre-resolution with the worker-pool, Chrome simply dispatches the getaddrinfo() call, which blocks the worker thread until the response is ready, at which point it just discards the returned result and begins processing the next prefetch request. Discards it? The result is cached by the OS DNS cache, which returns an immediate response to future, actual getaddrinfo() lookups. Simple, effective, works well enough in practice.

Well, effective, but not good enough! The getaddrinfo() call hides a lot of useful information from Chrome, such as the time-to-live (TTL) timestamps for each record, as well as the state of the DNS cache itself. To improve performance, Chrome team decided to implement their own, cross-platform, asynchronous DNS resolver.

By moving DNS resolution into Chrome the new async resolver enables a number of new optimizations:

  • better control of retransmission timers, and ability to execute multiple queries in parallel
  • visibility into record TTLs, which allows Chrome to refresh popular records ahead of time
  • better behavior for dual stack implementations (IPv4 and IPv6)
  • failovers to different servers, based on RTT or other signals

All of the above, and more, are ideas for continuous experimentation and improvement within Chrome. Which brings us to the obvious question: how do we know and measure the impact of these ideas? Simple, Chrome tracks detailed network performance stats and histograms for each individual profile. To inspect the collected DNS metrics, open a new tab, and head to chrome://histograms/DNS.

The above histogram shows the distribution of latencies for DNS prefetch requests: roughly 50% (rightmost column) of the prefetch queries were finished within 20ms (leftmost column). Note that this is data based on a recent browsing session (9869 samples), and is private to the user. If the user has opted in to report their usage stats in Chrome, then the summary of this data is anonymized and periodically beaconed back to the engineering team, which is then able to see the impact of their experiments and adjust accordingly. Rinse, lather, repeat.

Optimizing TCP connection management with pre-connect #

We have pre-resolved the hostname and we have a high likelihood navigation event that's about to happen, as estimated by the Omnibox, or the Chrome predictor. Why not go one step further, and also speculatively pre-connect to the destination host and complete the TCP handshake before the user dispatches the request? By doing so, we can eliminate another full round-trip of latency delay, which can easily save hundreds of milliseconds for the user. Well, that's exactly what TCP-preconnect is and how it works!

To see the hosts for which a TCP preconnect has been triggered, open a new tab and visit chrome://dns.

First, Chrome checks its socket pools to see if there is an available socket for the hostname, which it may be able to reuse - keep-alive sockets are kept in the pool for some period of time, to avoid the TCP handshake and slow-start penalties. If no socket is available, then it can initiate the TCP handshake, and place it in the pool. Then, when the user initiates the navigation, the HTTP request can be dispatched immediately.

Curious to see the state of all the open sockets in Chrome? Simple, head to: chrome://net-internals#sockets

Note that you can also drill into each socket and inspect the timeline: connect and proxy times, arrival times for each packet, and more. Last but not least, you can also export this data for further analysis or a bug report. Having good instrumentation is key to any performance optimization, and chrome://net-internals is the nexus of all things networking in Chrome - if you haven't explored it yet, you should!

Optimizing resource loading with prefetch hints #

Sometimes, the author of a page is able to provide additional navigation, or page context, based on the structure or the layout of their site, and help the browser optimize the experience for the user. Chrome supports two such hints, which can be embedded in the markup of the page:

  <link rel="subresource" href="/javascript/myapp.js">
  <link rel="prefetch"    href="/images/big.jpeg">

Subresource and prefetch look very similar, but have very different semantics. When a link resource specifies its relationship as "prefetch", it is an indication to the browser that this resource might be needed in a future navigation. In other words, effectively it is a cross-page hint. By contrast, when a resource specifies the relationship as a "subresource", it is an early indication to the browser that the resource will be used on a current page, and that it may want to dispatch the request before it encounters it later in the document.

As you would expect, the different semantics of the hints lead to very different behavior by the resource loader. Resources marked with prefetch are considered low priority and might be downloaded by the browser only once the current page has finished loading. Whereas subresource resources are fetched with high priority as soon as they are encountered and will compete with the rest of the resources on the current page.

Both hints, when used well and in the right context, can help significantly with optimizing the user experience on your site. Finally, it is also important to note that prefetch is part of the HTML5 spec, and as of today supported by Firefox and Chrome, whereas subresource is currently only available in Chrome.

Optimizing resource loading with browser prefreshing #

Unfortunately, not all site owners are able or willing to provide the browser with subresource hints in their markup. Further, even if they do, we must wait for the HTML document to arrive from the server before we are able to parse the hints and begin fetching the necessary subresources - depending on the server response time, as well as the latency between the client and the server, this could take hundreds and even thousands of milliseconds.

However, as we saw earlier, Chrome is already learning the hostnames of the popular resources to perform DNS pre-fetching. So, why couldn't it do the same, but go one step further and perform the DNS lookup, use TCP preconnect, and then also speculatively prefetch the resource? Well, that's exactly what "prefreshing" could do:

  • User initiates a request to a target URL
  • Chrome queries its Predictor for learned subresources associated with target URL and initiates the sequence of DNS prefetch, TCP preconnect, and resource prefreshing
  • If the learned subresource is in the cache, then its loaded from disk and into memory
  • If the learned subresource is missing, or has expired, then a network request is made

Resource prefreshing is a great example of the workflow of every experimental optimization in Chrome - in theory, it should enable better performance, but there are many tradeoffs as well. There is only way to reliably determine if it will make the cut and make it into Chrome: implement it and run it as A/B experiment in some of the pre-release channels with real users, on real networks, with real browsing patterns.

As of early 2013, the Chrome team is in the early stages of discussing the implementation. If it makes the cut based on gathered results, we may see prefreshing in Chrome sometime later in the year. The process of improving Chrome network performance never stops, the team is always experimenting with new approaches, ideas, and techniques.

Optimizing navigation with prerendering #

Each and every optimization we have covered up to now helps reduce the latency between the user's direct request for a navigation and the resulting page rendering in their tab. However, what would it take to have a truly instant experience? Based on the UX data we saw earlier, this interaction would have to happen in less than 100 milliseconds, which doesn't leave much room for network latency at all. What could we do to deliver a rendered page in sub 100 milliseconds?

Of course, you already know the answer, since this is a common pattern employed by many users: if you open multiple tabs then switching between tabs is instant and is definitely much faster than waiting for the navigation between the same resources in a single foreground tab. Well, what if the browser provided an API to do this?

  <link rel="prerender" href="http://example.org/index.html">

You guessed it, that's prerendering in Chrome! Instead of just downloading a single resource, as the "prefetch" hint would have done, the "prerender" attribute indicates to Chrome that it should, well, prerender the page in a hidden tab, along with all of its subresources. The hidden tab itself is invisible to the user, but when the user triggers the navigation, the tab is swapped in from the background for an "instant experience".

Curious to try it out? You can visit prerender-test.appspot.com for a hands on demo, and see the history and status of the prerendered pages for your profile by visiting: chrome://net-internals/#prerender

As you would expect, rendering an entire page in a hidden tab can consume a lot of resources, both CPU and network, and hence should only be used in cases where we have high confidence that the hidden tab will be used! For example, when you are using the Omnibox, a prerender may be triggered for the a high confidence suggestion. Similarly, Google Search sometimes adds the prerender hint to its markup if it estimates that the first search result is a highly confidence destination (aka, Google Instant Pages):

Note that you can also add prerender hints to your own site! However, before you do, note that prerendering has a number of restrictions and limitations, which you should keep in mind:

  • At most one prerender tab is allowed across all processes
  • HTTPS and pages with HTTP authentication are not allowed
  • Prerendering is abandoned if the requested resource, or any of its subresources need to make a non-idempotent request (only GET requests allowed)
  • All resources are fetched with lowest network priority
  • The page is rendered with lowest CPU priority
  • The page is abandoned if memory requirements exceed 100MB
  • Plugin initialization is deferred, and pre-rendering is abandoned if an HTML5 media element is present

In other words, prerendering is not guaranteed to happen and only applies to pages where it is safe. Additionally, since JavaScript and other logic may be executed within the hidden page, it is best practice to leverage the Page Visibility API to detect if the page is visible - which is something you should be doing anyway!

Chrome gets faster as you use it #

Needless to say, Chrome's network stack is much more than a simple socket manager. Our whirlwind tour covered the many levels of potential optimizations that are performed transparently in the background, as you navigate the web. The more Chrome learns about the topology of the web and your browsing patterns, the better it can do its job. Almost like magic, Chrome gets faster as you use it. Except, it's not magic, because now you know how it works!

Finally, it is important to note that the Chrome team continues to iterate and experiment with new ideas to improve performance - this process never stops. By the time you read this, chances are there will be new experiments and optimizations being developed, tested, or deployed. Perhaps once we reach our target destination of instant page loads (<100 ms), for each and every page, then we can take a break. Until then, there is always more work to do!

Making Decisions with Team Participation

Posted 4 months back at Dan Manges

As a team grows from a handful of people to a couple dozen, making changes with full team participation and buy-in becomes more difficult. However, if a team doesn’t overcome inertia and adapt, processes that worked well for a small team will start to break down, and the team will become less effective.

Common Approaches

Meetings and Email Threads

Involving an entire team of 20-30 people in a discussion is usually neither efficient nor effective at actually coming to a consensus about a change. Each person speaking for only a couple minutes would take nearly an hour.

Rather than scheduling a meeting, it’s possible to use asynchronous communication like email. Doing so has the benefits of allowing people to opt in or out of the discussion, while keeping everybody in the loop on the direction. It also may provide an opportunity for people to reflect on thoughts before responding, unlike what people are able to do in a real-time meeting. However, keeping an email thread on point can be challenging, and it’s easy for the dialogue to become unwieldy.

Hand Selecting Feedback

Team leaders often won’t want to make decisions unilaterally, so they’ll solicit feedback from a few members of the team. Getting feedback from a few people is better than implementing a change without any input, but it’s highly subject to bias. People generally have predisposition to a certain type of response. Some people are more agreeable, others are more confrontational. People will also differ in their passion for a certain topic: the most important issue to one person may be something that others don’t care much about.

Avoiding bias while soliciting feedback from specific people is difficult. Even if you try to talk to the people who may challenge you the most on a change, it may not represent the broader team opinion very well.

A Collaborative Approach

Set Expectations

The first thing to do when thinking about making a change is to communicate the problem that you’re trying to solve to the team. This provides two benefits. First, it sets expectations that something might change. People respond more favorably to change when they know it’s coming than when it seemingly comes from nowhere. Secondly, it creates an opportunity for people to try to solve the problem rather than debating a particular solution. Saying “I want to make this change” causes people to discuss the change. Saying “I want to solve this problem” leads people to discuss the problem and may result in a better solution.

Get Input

After communicating a problem statement, invite everybody to give input on the topic. At this point, try to avoid discussion and instead focus on gathering ideas. This process will identify who on the team is most passionate or invested in solving a certain problem. It also provides an opportunity to organize the ideas into themes before people start debating the merits of a certain solution.

Opt-In Meeting

After collecting feedback, set up a meeting for the people who are the most interested in discussing the problem. Ideally, this will involve no more than eight people. If more than that seem passionate about the topic, it might require a few separate meetings to give everybody an opportunity to voice their opinion. After discussing possible solutions, work on coming to a consensus. Try to end the meeting with a plan of action, or at the least a narrowed-down list of ideas.

Broader Team Feedback

Communicate the result of the meeting with the rest of the team. Let everybody know what the group decided, and give people another opportunity to provide feedback. At this point if the team buys-in to the approach, then execute the change. If there are concerns with the proposed solution, consider the magnitude of the concern. If only one person dissents, talk with them individually. If a bunch of people do, you may need to regroup to discuss in person. Keep iterating on the cycle of soliciting feedback, discussing potential changes, and proposing them to the team until there’s a consensus. Each round can involve different people as individuals choose to opt in or out.

Execute

Once you have a consensus, make the change. Hopefully, the team regularly meets for a retrospective, so there will be an opportunity to discuss whether the change has been effective at solving the identified problem once enough time has passed to give it a chance. If the change isn’t effective, repeat the process. You should be able to go through these phases fairly quickly; it doesn’t have to be a long, drawn out cycle.

Democracy

I mentioned that it’s ideal for the team to come to a consensus, but that’s not always possible. Also, a team lead may need to make a decision that goes against the popular vote of the team. Decision making by democracy isn’t always the best model due to information asymmetry. If that happens, it’s still valuable to take the time to get input on the proposed change. It provides an opportunity to address concerns, provide missing context, and communicate the circumstances that motivate a certain course of action.

Ruby Science: Avoid Divergent Change and Use Convention Over Configuration

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

We have two new chapters to announce this week in Ruby Science. If you’re already reading Ruby Science, make sure to log into GitHub and download the latest version.

With this week’s updates, you’ll learn how to keep your classes from becoming junk drawers by learning to avoid Divergent Change. You’ll also see an example of using Convention Over Configuration to remove tedious boilerplate and avoid Duplicated Code.

The book is a work in progress, and currently contains around 82 pages of content. Purchasing the book also gets you access to the companion example application, as well as the ability to send thoughtbot your toughest Ruby, Rails, and refactoring questions.

If you haven’t already purchased it, you can still get access for the early purchase price of $39.

This Friday, the price will increase to $49.

Get your copy of Ruby Science today.

Episode #340 - January 29th, 2013

Posted 4 months back at Ruby5

Devise security, secure cookie security, JSON Parser security.. do you see a theme? Then there's Mutant, Von, Garelic, Dossier, Split, Inkwell, Social Stream, Faster Rails, and Rails resources.

Listen to this episode on Ruby5

This episode is sponsored by Top Ruby Jobs
If you're looking for a top Ruby job or for top Ruby talent, then you should check out Top Ruby Jobs. Top Ruby Jobs is a website dedicated to the best jobs available in the Ruby community.

Devise Security Update
Plataformatec has released an important update to Devise, particularly if you’re using something other than Postgres or SQLite. They've released updates to the 1.5, 2.0, 2.1, and 2.2 levels of the library.

Mutation testing with Mutant
You might have 100% code coverage, but that doesn’t mean your tests are great. Just because a line of code is getting executed for a test doesn’t mean that you’re testing the right behavior of that code. Mutant takes care of making small changes to your code at runtime to and then makes sure your tests properly fail.

Von
Travis Dunn created a library called Von, which uses Redis to increment anything you want in a simple key value table. You can do some pretty advanced analytic tracking and querying.

Garelic
Ján Suchal created a GARelic gem, which allows you to do performance monitoring on your Rails application using Google Analytics. You can monitor things like response times for any given action, find slow page loads, and see how much time is spend in the database and view generation for each action.

Dossier
If you have a data-intensive app, and you need to produce a variety of reports from different angles, maybe check out Dossier by Adam Hunter. Dossier offers a straightforward way to generate html reports out of your SQL tables.

Split
Split, a Rack based AB testing framework, recently came out with version 0.5. If you really want full control over your AB experiments and your data, Split comes with an impressive number of features and algorithms to do it yourself.

Rails 4 security for session cookies
Rails 4 is enhancing the security options for session cookies you use in your apps. Caike Souza shows how Rails 4 will let you encrypt cookies, making them hard to read, as well as hard to change.

Inkwell
If you need to build a Rails app that has social networking features like comments, reblogs, following/followers, and timelines you may want to check out Inkwell by Sokolov Sergey. The gem comes with models which easily can give your app this behavior.

Social Stream 1.0
The Social Stream gem recently hit 1.0. This gem also adds social networking features to your existing app, but takes things even further with default controllers and views, as well as a bunch of additional components, such as documents, events, links, and contacts

Faster Rails
Since the beginning of January Tom Fakes has been writing some great blog posts on speeding up your Rails apps, from Fragment cache techniques to etags. He’s also started a free monthly newsletter called Faster Rails.

7 Ruby Resources that’ll keep you coming back
If you’re looking for additional Ruby educational content.. Bart Oleszczyk emailed us over the weekend about a blog post on 7 Ruby resources to keep you up to date.

JSON Parser Vulnerability in Rails 3.0 & 2.3
There is a vulnerability in the JSON code for Ruby on Rails which allows attackers to bypass authentication systems, inject arbitrary SQL, inject and execute arbitrary code, or perform a DoS attack on a Rails application.

Pairing for Developer Interviews

Posted 4 months back at Dan Manges

The hardest part of building a successful, fast-growing software company is assembling the team to do it. It’s been a while since I read Good to Great, but a friend recently reminded me of this quote:

“I don’t know where we should take this company, but I do know that if I start with the right people, ask them the right questions, and engage them in vigorous debate, we will find a way to make this company great.”

To hire the right people, teams need to spend time thinking about their interview process. A successful result means the team ends up hiring the right people and passing on people who wouldn’t be a good fit. It’s a costly mistake, however, to hire somebody who doesn’t work out, or to pass on somebody who would have been great. Given that most fast-growing software companies want to hire as many great developers as they can find, interviewing becomes a frequent and crucial process. To increase likelihood of success, it’s important to have a collaborative process. Although teams may be able to save time by having only a single person participate in each interview segment, pairing can give the team many of the same benefits that come from pair programming.

Exposure

Exposing as many people from the team to the candidate is helpful for both the team and the candidate. For the team, people will have different insights and perspectives that help make a good decision when getting together at the end of the process to decide whether or not to make an offer. Interview processes are bidirectional, though. For the candidate, he or she is getting an opportunity to meet as many of his future coworkers as possible. Although it’s hard to get to know somebody in an interview setting, it’s nice to be able to meet several team members.

Training and Feedback

Interviewing is a skill, so it’s something that people need to learn how to do. Pairing a seasoned interviewer with somebody who is new to the process is a great way to provide them with training.

Even for experienced interviewers, it’s helpful to get feedback on how they approached the interview. If an interviewer is talking to a candidate in isolation, nobody can offer any feedback.

Values

Teams should align on their values and the relative importance of them. Is it more important to get somebody with strong depth in technology or the ability to learn quickly? Ideally, you look for a candidate with both, but which one is more important for the position and team? Despite striving for alignment, people will maintain their own preferences and biases. Having more people involved in the process will create more diversity in perspectives, which a healthy team should be able to use to debate and come to a consensus.

The discussion on perspectives can also help standardize viewpoints. Just as pair programming helps developers establish conventions for a project so that multiple developers aren’t pulling in different directions, pairing for interviews helps establish standards for what to look for in candidates. You can try to do this without having both people in the room at the same time, but it’s harder that way. It’s similar to how it’s easier to discuss code when two developers are working on a pairing station than it is to have a phone conversation about it.

Flow

Occasionally when interviewing, there can be a communication breakdown between the interviewer and the candidate. I’ve especially seen this happen when the two people are accustomed to using different terms to describe the same thing. The interviewer asks a question, and the candidate doesn’t quite understand. The interviewer doesn’t understand why the candidate doesn’t understand, and tries to restate the question in a way that still doesn’t help. In those situations, it’s typical to move on to a different question, but that may leave the candidate feeling like he or she responded poorly on that topic. Having a second interviewer in the room gives somebody else an opportunity to engage in the conversation to try to clarify.

It’s also common to have a lull in a conversation when you run out of questions related to the current topic and need to figure out where to transition to next. Having a second interviewer in the room creates an opportunity for the other person to pick up the conversation and start down another track.

Decision

Ultimately, at the end of the interview, the team needs to come to a consensus. It’s easy when the vote is a unanimous yes, but it’s much harder when the vote is split. If a single interviewer thought that a candidate did poorly on a certain interview segment, it could be difficult for the rest of the team to overrule that. With a second opinion, it becomes easier. Either both interviewers agree, and the decision to pass on the candidate becomes easier, or the interviewers are split, and there are multiple perspectives to consider. Either way, the additional data is helpful.

Interviewing is challenging. I’ve often heard it compared to getting married after a single date. The decision to hire somebody can have a greater impact on the team and company than most other decisions that are made on a daily basis. Having multiple people involved will increase the time commitment necessary to interview a candidate, but it’s well worth the investment.

Thanks

Thanks to Braintree developers Dave Pirotte, Adam Forsyth, and John Downey for sharing some of their thoughts on the benefits of pairing in a recent team discussion.

Episode #33: I've failed before

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

Episode #33: I've failed before:

This week on the podcast, Ben Orenstein is joined by Jarrod Drysdale, the author of Bootstrapping Design. Ben and Jarrod discuss the sales and revenue of the book, and his new project, cascade.io. They also talk about learning new things, problem solving, and the differences between programming and design. They also discuss the downside to recurring revenue, successful marketing strategies for his book, advice for people who want to start something new, the concerns of a solo entrepreneur, and how his previous failures help him keep perspective.

#blue opens for business

Posted 4 months back at Tom Ward

<article> <header>

#blue opens for business

</header>

If you’re an O2 customer based in the UK, you might be interested that #blue, a project we’ve built, has recently (re)opened for business. It’s a soft launch, but feel free to tell your friends.

#blue gets copies of every SMS message you send or receive, and makes them available to you on the web. You can search your messages, read whole conversations, and even reply, all from the comfort of your browser.

As well as a nice-looking web UI, there’s also an API that allows other apps to read and manipulate your messages and contacts (once you give them permission). Think automatic posting to twitter, or showing new messages as alerts on your desktop. The possibilities are endless.

Oh, and it’s all free. All you need is an O2 phone.

If you think it sounds interesting, ask for a beta request. You should get an invitation very quickly. Once on board, please send me any suggestions and feedback. It’s going to be interesting to see where we can take this project.

<footer> 3rd March 2011 </footer> </article>