Sunday, 23 September 2007

Manipulation and learning

There's been a thread on the Agile Sweden mailing list this week about the kind of manipulation that consultant-coaches use in the course of their work with a software development team. Several people seem to favour this definition of what manipulation is:
"One view, to paraphrase Kent Beck, is that manipulation is when you wouldn't get
the same effect if you were upfront about the intervention."

(This quote is attributed to Michael Feathers)
Someone pointed out that they recently went on a training course where during one of the exercises, the course leader was not up front about its purpose. Afterwards he felt that this manipulation had led to a new insight that he was thankful for.

This reminded me of an experience I had many years ago on a similar training course, which was long before I or anyone else had ever heard of agile. As software consultants we were supposed to be learning about the social aspects of our profession, and it was about the third day. The instructors had been interleaving lectures with little games and activities designed to illustrate what we had been learning. The exercise that sticks in my mind went like this.

We were instructed to divide into two groups, and each group was given a datasheet of information and a description of a problem to solve. We were put in separate rooms and given a suitable time limit. We were encouraged to examine the problem, the related data, and report back a suitable solution to the problem to the instructor within the time limit.

In our group we carefully read through the problem and datasheet, and using a whiteboard made connections between the various pieces of information. We quickly discovered that there were large gaps in the information we had and that it was rather difficult to solve the problem. We questioned our instructor about this but she said she had no further information for us. We used our initiative, we thought laterally and came up with what we thought was a reasonable solution. Towards the end of the allotted time we reported back to our instructor our best guess at a solution. She told us that it was wrong.

The other team had not managed to solve their problem either, and when we joined together to discuss what had happened, it turned out that both teams had actually been given the same problem to solve, but each had only half of the needed data. So the only way to successfully complete the exercise would have been for the two teams to pool their knowledge and come up with a joint solution.

I have to say that after 3 days of these kind of exercises I was pretty fed up and I wasn't the only one. There was a general feeling of being cheated and misled and that we had been set up to fail. In most of the other exercises we had done, talking to the other team would have been considered cheating, since in several of them had been set up as competitions.

The instructor explained that what she wished us to learn from this exercise was that in real life, if your team doesn't have enough information to be able to write the software, go and talk to the customer. This is a good thing to learn. However, it is not the insight I took away from the exercise. For a start, in real life you know who the customer is and if you don't have enough information, you know it is not against the rules to go and ask.

In general, I think that this kind of teaching technique can work really well and be a good antidote to endless lectures. However I think it can be done badly, as in this case. If your students come away feeling angry, cheated and misled, they are not going to be very receptive to your lesson, however well intentioned.

Wednesday, 19 September 2007

Of Sue and Squirrel

Let me tell you a story about a young developer called “Sue”.

Sue has just learnt all about eXtreme Programming. She has worked on an XP team with lots of pair programming, unit testing, refactoring, simple design etc and is totally converted. The world should be doing XP!

Sue hears about another application at her company which is in trouble. Let's call the application “Squirrel”. Squirrel has been in production for a few years now, the codebase is rather large and the internal design is somewhat confused. The Squirrel team is having increasing difficulty adding the features the customer wants in a timely manner. Sue is convinced what they need to do is to refactor the codebase and add lots of automated tests.

Sue manages to persuade her manager “Paul” to let her work on refactoring Squirrel. She gets a copy of the code out of the version control system and sets about writing unit tests and refactoring. A few weeks go by and Sue reports to Paul that everything is going really well. She has solved some significant technical challenges in order to put in place some unit tests and has a small suite that she runs regularly. She proudly shows Paul the test scores and some UML diagrams of how the code looked before her refactoring, and after. Paul is pleased at how much better the UML looks and when Sue asks for more time to improve the code further, he grants her request.

A few more weeks pass and Sue is starting to attempt quite ambitious refactorings. She really feels the Squirrel team is going to be delighted when they see how much better their code looks and how many tests it has.

Meanwhile the Squirrel team has been continuing to add features and fix bugs and are actually quite proud of the progress they have made with the small team available to them. The users of the Squirrel application have complimented them on how much more stable and usable the application is becoming.

After a few months, Sue, Paul and the Squirel team agree that the next release of Squirrel should include Sue's refactorings. The approach chosen is for the Squirrel team to take Sue's copy of the codebase, and integrate all the recent bug fixes and new features into it. Everyone thinks this will be straightforward using the advanced diff&merge tools provided with the version control system. Sue persuades Paul that this would be better than applying all of Sue's refactorings to the Squirrel team's version of the codebase, since she had made so many sweeping changes and her codebase had extensive unit tests.

It was a total disaster. Sue had introduced a huge number of bugs when refactoring. Sue didn't understand what the code was really doing, having never actually worked on the application before and having only the documentation to go on. She had hardly spoken to the Squirrel team in all the months she had been working on their code, even though their project room was a short walk from her desk. The unit tests she had written covered only a fraction of the codebase and often reflected her poor understanding of what the code ought to be doing.

The diff between the two branches of the codebase was huge and the integration work was difficult, time consuming and error prone. After a week or so the work was stopped, Sue was in the doghouse, “Refactoring” was a dirty word, and the Squirrel team went back to their old codebase.

So what went wrong? Sue clearly did not have enough understanding of the codebase in question and was not skillful enough to pull off the refactorings she attempted. She had too much faith in the few unit tests she had written and lent on them too much. Sue didn't think about the integration cost rising exponentially the longer she worked apart from the Squirrel team.

However, I think it is Paul who really carries the blame for what happened. As the manager responsible for both Sue and the Squirrel team, he should not have allowed Sue to continue working alone for weeks on end producing only UML diagrams and unit test reports. Paul should have anticipated the integration problems even if Sue didn't. He should have asked to see working code – if he had sat down and manually tested Sue's copy of Squirrel, he would have found serious bugs within minutes.

Well, thankfully Sue wasn't sacked immediately and was given a chance to redeem herself. Sue joined the Squirrel team and started working on bug fixes and customer features. She quickly gained more understanding of the code than she had ever got working alone. Sue did put in place a few unit tests and do some refactoring, but it was always in the context of whatever bugfix or feature she was working on at the time. Most of the big sweeping refactorings that Sue thought would make a difference were never implemented.

The customer was happy for a while but the pace of new feature addition was still a bit slow for them. The customer also discovered that they could get a much better product from a competitor. So a couple of years later they dropped Squirrel. Happily for Sue, by then she had found a better job with a more competent manager.

Tuesday, 18 September 2007

KataMinesweeper acceptance tests.

I was inspired by the SaoPauloDojo to have a look at KataMinesweeper. The code they came up with is published amongst the files on their googlegroup, and I am referring to the one called 08-MineSweeper.py. Of course, it being in portuguese I am obviously missing many subtleties :-)

At the SaoPauloDojo meeting they clearly decided to start with the most interesting part of the problem, ie counting mines, and didn't get to the part that deals with input and output strings. I think this is a shame because they have jumped straight to writing tests that assume the minefield data will be a list of lists. I am not convinced this is the best data structure, and all the tests they have written are very coupled to this design decision. For example:

class TesteAceitacaoCampoMinado(unittest.TestCase):
def testeCampo3x3Vazio(self):
campoMinado = CampoMinado([[0,0,0],[0,0,0],[0,0,0]])
campoMinado.preencheTabuleiro([['.','.','.'],['.','.','.'],['.','.','.']])
self.assertEquals([[0,0,0],[0,0,0],[0,0,0]], campoMinado.matriz)

def testeCampo3x3BombaNoMeio(self):
campoMinado = CampoMinado([[0,0,0],[0,0,0],[0,0,0]])
campoMinado.preencheTabuleiro([['.','.','.'],['.','*','.'],['.','.','.']])
self.assertEquals([[1,1,1],[1,'*',1],[1,1,1]], campoMinado.matriz)

def testeCampo2x3ComUmaBomba(self):
campoMinado = CampoMinado([[0,0,0],[0,0,0]])
campoMinado.preencheTabuleiro([['.','.','.'],['*','.','.']])
self.assertEquals([[1,1,0],['*',1,0]], campoMinado.matriz)


I think that tests at the acceptance test level should be as independent as possible of these kinds of design decisions. Not only that, the acceptance tests should be readable to a non-coding domain expert. I would prefer to see tests that look more like this

   
def testSmallEmptyGrid(self):
input = """\
3 3
...
...
...
"""
expected = """\
Field #1:
000
000
000
"""
self.failUnlessEqual(expected, minesweeper.count_mines(input.splitlines()).next())

But then of course one of the first problems you have to tackle is how to handle the I/O rather than the perhaps more interesting problem of how to count the mines.

In the problem description the acceptance tests are already set up as plain text input and output, so for me it would be very natural to tackle the problem using the tool TextTest to drive development instead of unit tests. This is a technique that Geoff and I have been working on recently, we presented a workshop together at XP2007 and Geoff by himself at agile2007 exploring this idea. By using this technique you isolate your acceptance tests from the current implementation of the code and I think this makes you more agile. You can refactor your code as much as you like so long as the external acceptance tests still pass. I'm working on an article which will showcase this technique that hopefully I will get around to finishing soon.

I'm also planning to tackle this Kata together with my local dojo group - the Gothenburg Ruby User Group (GRUG) tomorrow evening. We are planning to use Ruby, RSpec and BDD, so it should be interesting to see what we come up with and how decoupled our tests are from the internal design of the code.

Friday, 14 September 2007

First post

Hello.
I'm a bit nervous actually. I haven't had a blog before. I'm hoping that it will turn into a place for me to write interesting articles about coding, which is an activity I enjoy. (I enjoy cooking too but I don't expect I will be writing about that so much.)

I hope I might comment on stuff other people write on the web. I also hope my writing skills might improve by doing so. So we'll see. I think someone wrote that the average blogger gives up after about 3 months so that might happen too. I am expecting a baby in about 8 weeks time so I may not even last that long. Anyway, my next post should be more interesting than this one.