Effective vs Ineffective Pair Programming

Pair programming has always been one of the more controversial agile practices. Mostly because it is damn hard to sell to management (at least the unenlightened kind of management), we’ve all heard the old “double the cost for the same work” excuse. To those in-the-know that excuse is patent nonsense, those who’ve done it are well aware of the benefits that it brings even if they do sometimes find it hard to verbalise just exactly what those benefits are.  Pairing brings all sorts of benefits:

  • organic code review
  • less bugs, which means less maintenance effort
  • detecting problems early
  • more focused effort (harder to procrastinate when person next to you)
  • etc.

The most important benefit in my opinion is the fact that pairing is highly conducive to organic knowledge transfer (“pairing is knowledge sharing” for the poet in you). I believe this is key since in a large system there is literally no other way to do this well.

It is however interesting to ask exactly why pair programming brings those benefits and this would directly lead us to ask if there are any situations where pair programming can be less effective and what we can do to mitigate that.

What’s So Good About Pairing And Why It Works

The  reason I think pairing works so well is the fact that the “driver” and the “navigator” are mentally in completely different places. Andy Hunt explains this really well in his “Pragmatic Thinking & Learning – Refactor Your Wetware” book. The driver is focused on the nitty-gritty of the code, his mind is in its analytical mode (his left brain is more engaged) and he is more focused on the “small picture”. The navigator on the other is more focused on the bigger picture and due to the fact that he is not actually busy driving his mind is in its more intuitive mode (his right brain is more engaged). This means that the navigator is more able to see patterns and abstractions that the more analytically engaged driver will tend to miss.

However in order to be able to see the patterns and notice the abstractions, good system knowledge is invaluable (and the best way to gain it is through pairing so this is sort-of cyclical), which brings us to 4 situations where pairing can be either less or more effective.

The 4 Pairing Situations

The 4 situations are as follows:

  • the driver and the navigator both know the system/area well
  • the driver knows the system/area well, while the navigator does not
  • the navigator knows the system well while the driver does not
  • both the driver and the navigator do not know the system/area well

Driver and the navigator both know the system/area well

This is the ideal situation (obviously), and there is no need to say to much about this, when pairing having both people well versed in the system will ensure maximum effectiveness.

Driver knows the system/area well, while the navigator does not

This is a lot less ideal, in this case the person with the most knowledge of how the system works is in their analytical mode and is a lot less able to see the bigger picture and pick up on the appropriate patterns and abstractions. More than that, since as the driver they are focused on the task at hand and are actively busy/engaged they are potentially less able to share their expertise with their partner. The navigator is also extremely hampered by their lack of system knowledge, not only are they limited in their ability to help the driver, but they will also tend to feel frustrated and unfulfilled.

Navigator knows the system well while the driver does not

This situation is a lot better. In this case the person with less system knowledge is fully engaged as the driver, the navigator on the other hand is able to guide the pairing effort and is still able to see the code from a right-brain perspective. The navigator is also able to maintain a stream of commentary about the system for the driver to absorb. The downside here may be that the work proceeds somewhat slower due to the lack of system knowledge on the part of the driver. This can however be mitigated by discussion between the members of the pair before proceeding to do the next step in the work.

The driver and the navigator do not know the system/area well

From the perspective of getting some work done this is the worst situation to find yourself in. It can however be quite a lot of fun to dig through code you don’t know, as well as find and recover from mistakes together. The danger here is due to the fact that neither person is potentially able to appreciate the bigger picture –  the quality of the work can suffer and it can be inconsistent with the rest of the code in the system.

It is difficult to mitigate lack of system knowledge, there is always a significant amount of time and effort involved when the system is of reasonable size. I believe there are 2 ways to ensure that pairing brings benefit no matter what the situation:

  • the person with the most system knowledge should spend the majority of their time being the navigator rather than the driver, in this way they can bring the most benefit to the system as well as to their partner
  • both members of a pair need to ensure that they are constantly proactive both in learning and in teaching

Do you think there are other ways to make sure that our pairing efforts are always optimally successful? Feel free to leave a comment and let me know.

How To Write A Name Generator (In Ruby)

I love reading fantasy, I’ve even written about some of my favourite fantasy series on this blog. One of the things that I have always found interesting about fantasy literature (besides unworkable economies and unsustainable population densities – I tend to over-analyse when I read :)) was how they come up with the names for all the characters. Large fantasy series often contain hundreds of characters – that’s a lot of names. This line of though naturally led me to think of what I would do if I ever needed to make up a bunch of names and being the software developer that I am the answer was naturally – get my computer to make up the names for me.

If you do a search around the web for name generators you get quite a few results, unfortunately most of those don’t tell you how they do what they do and even that is besides the point since I wasn’t really happy with the results that most of these name generators produce. Either the results are way too random (how about 6 consonants in a row) or they are not random enough with clear traces of human intervention (i.e. choosing from a list of pre-made names). Then I found Chris Pounds excellent name generator page. One of the things that he has on this page is his language confluxer (lc) script so for my first attempt at writing a name generator I decided to basically take his script and clean it up a little bit. There were two reasons for this:

  • he uses a pretty clever algorithm for his name generator, it is completely data driven and is therefore able to avoid the 6 consonants/vowels in a row issue while producing output that sounds similar to the data it is based on
  • it was a yucky Perl script and nobody wants to work with that (except Perl programmers), so I felt it was my duty to make it a little bit nicer and since I’ve been playing around with Ruby lately, well you get the picture :)

The Name Generator Algorithm

As I said the script is completely data driven in that it takes a list of words (names in our case) as input and uses these to produce a bunch of randomised names that hopefully sound similar to the original input. It does the following:

  • produces a list of starting letter pairs from the input data (all our names will start with one of these pairs)
  • produces a map of which letters can follow which other letters based on the input data
  • generates words/names by randomly selecting a starting pair and then appending to the word by randomly choosing a letter from the map based on what the last letter in our new word currently is
  • this continues until the word length falls into a particular range (this range is hard-coded in the script)

There are a few more little twists that make this whole thing function but that is the essence of the algorithm.

Faithful Perl-to-Ruby Conversion

So first thing I did was to take the Perl script and do a direct conversion into Ruby, here is what I got:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
require 'getoptlong'
 
data_file = 'data.txt'
words_to_generate = 10
 
min_length = 3
max_length = 9
 
opts = GetoptLong.new(
  ["--datafile", "-d", GetoptLong::OPTIONAL_ARGUMENT],
  ["--number-of-words", "-n", GetoptLong::OPTIONAL_ARGUMENT]
)
 
opts.each do |opt, arg|
  case opt
  when '--datafile'
    data_file = arg
  when '--number-of-words'
    words_to_generate = arg
  end
end
 
start_pairs = []
follower_letters = Hash.new('')
 
File.open(data_file, 'r') do |file|
  chars = file.read.chomp.downcase.gsub(/\s/, ' ').chars.to_a
  chars.push(chars[0], chars[1])
  (chars.length-2).times do |i|
    if chars[i] =~ /\s/
      start_pairs.push(chars[i+1, 2].join)
    end
    follower_letters[chars[i, 2].join]=follower_letters[chars[i,2].join]+chars[i+2,1].join
  end
end
 
def generate_word(word, follower_letters, min_length)
  last_pair = word[-2, 2]
  letter = follower_letters[last_pair].slice(rand(follower_letters[last_pair].length), 1)
  if word =~ /\s$/
    return word unless word.length <= min_length
    return generate_word(word[-1, 1]+letter, follower_letters, min_length)
  else
    word = word.gsub(/^\s/, '')
    return generate_word(word+letter, follower_letters, min_length)
  end
end
 
words_to_generate.times do |i|
  puts generate_word(start_pairs[rand start_pairs.length], follower_letters, min_length)[0, max_length].capitalize
end

At this point I had a bit of a shock at how eerily similar the Ruby version of the script looks compared to the Perl version (*shudders*). Anyways, you can just take the above script put it into a file and run it, you’ll need to give it a data file (here is the one I used).

Cleaning Up The Basic Name Generator

The problems with the above script are:

  • it is not self-documenting
  • it is hard to test
  • it is hard to extend

Anyways, I decided to make it a little bit nicer and easier to play around with by breaking it up into a couple of classes (in the interest of object orientation and stuff):

  • name_generator_main.rb – the script entry point
  • NameGenerator – concerned with name generation (as you might expect)
  • DataHandler – concerned with reading the input data and producing the maps and arrays on which the NameGenerator relies
  • ArgumentParser – concerned with dealing with the command line arguments

You can download all of it here.

Now the main script looks much cleaner and you know exactly what’s happening just by reading it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
require 'argument_parser'
require 'data_handler'
require 'name_generator'
 
argument_parser = ArgumentParser.new
argument_parser.parse_arguments
data_handler = DataHandler.new
data_handler.read_data_file(argument_parser.data_file)
name_generator = NameGenerator.new(data_handler.follower_letters)
names = name_generator.generate_names(argument_parser.words_to_generate, data_handler.start_pairs)
names.each {|name| puts name}

This produces output similar to the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Jamin
Luce
Jevon
Fredy
Hamilinis
Emmano
Shamarcul
Gagaedric
Jary
Raelis

That’s pretty damn good for a random name generator. The best part of it, since it is completely data driven, if you change the input data you completely alter the output. So if you pass in a file with a bunch of French names, you will get French-sounding random names etc. Try it yourself!

Downgrading A Ubuntu Package

I was recently upgrading a couple of Ubuntu machines to the latest and greatest everything. It’s good to be up-to-date makes me feel like a no-nonsense, can-do developer :). Anyways one of the latest and greatest things that Ubuntu pulled in for me was the latest java 6 (JDK 1.6.0_14). All of a sudden my build started failing with strange coverage errors – I hate it when that happens. After looking around for a bit I found that there was a Cobertura issue with java 1.6.0_14, this one. So I though I’d try and downgrade Java 6 to the previous version (who needs to be fully up-to-date anyway, that stuff is for the birds, the real hardcore developers use the penultimate version, yeah!).

This of course was easier said than done, Ubuntu doesn’t really like it when you try to downgrade stuff. I thought that if I could obtain, or create, a debian package with the version of java that I was after I might just be able to manually install that one (yeah that might work). I was wrong, I did get my hands on the debian package that I needed, but Ubuntu spewed out some gaff about dependencies and … long story short, it didn’t work. Of course I could always forgo the use of the package manager and just manually install java and set up the PATH and JAVA_HOME and so forth but that would make me feel a little unclean so I thought I’d persist and do things the easy hard way.

Eventually (after searching around for ages and eating 2 pieces of candy – Vicks VapoDrops – good for the throat) I found a nice Ubuntu forum thread about how to downgrade a package (this one), so here is the summarised version of how to downgrade a Ubuntu package – more specifically my java 6.

Firstly, we need to find what lower versions of our package are available to us, we use the following command:

1
apt-cache showpkg sun-java6-jdk

This will produce output similar to the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Package: sun-java6-jdk
Versions:
6-14-0ubuntu1.9.04 (/var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_jaunty-proposed_multiverse_binary-amd64_Packages) (/var/lib/dpkg/status)
Description Language:
File: /var/lib/apt/lists/security.ubuntu.com_ubuntu_dists_jaunty-proposed_multiverse_binary-amd64_Packages
MD5: cb47d4c49fa7317c472613b7010ef9d8
6-13-1 (/var/lib/apt/lists/au.archive.ubuntu.com_ubuntu_dists_jaunty_multiverse_binary-amd64_Packages)
Description Language:
File: /var/lib/apt/lists/au.archive.ubuntu.com_ubuntu_dists_jaunty_multiverse_binary-amd64_Packages
MD5: cb47d4c49fa7317c472613b7010ef9d8
Reverse Depends:
sun-java6-source,sun-java6-jdk 6-14-0ubuntu1.9.04
sun-java6-javadb,sun-java6-jdk 6-14-0ubuntu1.9.04
sun-java6-demo,sun-java6-jdk
sun-java6-demo,sun-java6-jdk 6-14-0ubuntu1.9.04
libnb-java2-java,sun-java6-jdk
libnb-java2-java,sun-java6-jdk
sun-java6-source,sun-java6-jdk 6-13-1
sun-java6-javadb,sun-java6-jdk 6-13-1
sun-java6-demo,sun-java6-jdk
sun-java6-demo,sun-java6-jdk 6-13-1
glassfishv2-bin,sun-java6-jdk
glassfishv2,sun-java6-jdk
Dependencies:
6-14-0ubuntu1.9.04 - sun-java6-bin (5 6-14-0ubuntu1.9.04) libc6 (0 (null)) libx11-6 (0 (null)) debconf (18 0.5) debconf-2.0 (0 (null)) sun-java6-demo (0 (null)) sun-java6-doc (0 (null)) sun-java6-source (0 (null))
6-13-1 - sun-java6-bin (5 6-13-1) libc6 (0 (null)) libx11-6 (0 (null)) debconf (18 0.5) debconf-2.0 (0 (null)) sun-java6-demo (0 (null)) sun-java6-doc (0 (null)) sun-java6-source (0 (null))
Provides:
6-14-0ubuntu1.9.04 - java6-sdk java5-sdk java2-sdk java2-compiler java-sdk java-compiler
6-13-1 - java6-sdk java5-sdk java2-sdk java2-compiler java-sdk java-compiler
Reverse Provides:

There are two versions available in the repository. As we can see the java version we are looking for (6-13-1) is here. However it is worth noting that had we wanted to downgrade several versions back, we would be out of luck as older versions are no longer in the repository.

Now that we see the version that we want we need to do the following to downgrade:

1
sudo aptitude install sun-java6-jdk=6-13-1

This will produce something along the lines of:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Reading package lists... Done
Building dependency tree
Reading state information... Done
Reading extended state information
Initializing package states... Done
The following packages are BROKEN:
sun-java6-jdk
0 packages upgraded, 0 newly installed, 1 downgraded, 0 to remove and 2 not upgraded.
Need to get 17.7MB of archives. After unpacking 1925kB will be freed.
The following packages have unmet dependencies:
sun-java6-jdk: Depends: sun-java6-bin (= 6-13-1) but 6-14-0ubuntu1.9.04 is installed.
The following actions will resolve these dependencies:
Remove the following packages:
sun-java6-fonts
sun-java6-plugin
Downgrade the following packages:
sun-java6-bin [6-14-0ubuntu1.9.04 (jaunty-proposed, now) -> 6-13-1 (jaunty)]
sun-java6-jre [6-14-0ubuntu1.9.04 (jaunty-proposed, now) -> 6-13-1 (jaunty)]
Score is 188
Accept this solution? [Y/n/q/?]  Y
The following packages will be DOWNGRADED:
sun-java6-bin sun-java6-jdk sun-java6-jre
The following packages will be REMOVED:
sun-java6-fonts{a} sun-java6-plugin{a}
0 packages upgraded, 0 newly installed, 3 downgraded, 2 to remove and 2 not upgraded.
Need to get 50.7MB of archives. After unpacking 4096kB will be freed.
Do you want to continue? [Y/n/?] Y

If we now run:

1
java -version

We get:

1
2
3
java version "1.6.0_13"
Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
Java HotSpot(TM) 64-Bit Server VM (build 11.3-b02, mixed mode)

Which is the downgraded java that we were after, sweet!