I used to keep coding dojos for my colleagues, and it was sometimes very hard to find the right topic. I recently rediscovered the code kata – I did it earlier, but stopped after a short period -, and got an idea. I’m going to use it for discovering areas where our teams can improve. The setup is very easy: I propose a certain kata to the others with a time constraint. While they are working – the participation is voluntary -, I’m hanging around and looking for certain clues. These clues and the discussions after the session will help me find the areas where we need to exercise more.
After they get used to the code kata style and are able to finish the exercise within the time frame, we go forward with some constraints. In the far future, they will be able to pick their own code kata and constraint, but first there are things which have to be re-learnt and practised more. Here are some of the usual problems:
My plan is to come out with good practices in order to show a way to improve these areas, but for now, let’s have a final look at the code kata setup:
Present a coding exercise for the participants
Give them a time frame and keep them informed about the elapsed time
Walk around and watch out for things the guys are doing right and wrong
When the time has elapsed, shortly discuss the experience, share ideas with each other (sharing is very important here)
If there is time left, redo the session from the start
…
We are going to keep these sessions twice a week before the daily stand-up meetings. Usually this time of day is less productive, because not everybody is in the office, people are warming up, having their starting tea or coffee – the list goes on. It is also a good time for doing improvements, but I’ll talk about that in a different post.
One can ask why this style is different from others, and how it is going to help. I see it this way: if programmers have to perform a simple but challenging task under time pressure, they are focusing on it very much. They won’t bother that you are watching what they are doing, or that you are asking them short but focused questions. On the other hand, during pair programming, they may show a picture of themselves as they want to look like, or simply become shy. Time will tell how this is going to work. I’ll keep you posted.
There are several advantages and disadvantages of Test Driven Development. In this post, I have no intention of repeating any of these, instead, I’d like to show away to use TDD effectively while changing legacy code.
The Problem
Have a look at this legacy code:
packagecom.zsoltfabok.dojo.legacy.stepback;publicclassComparator{publicboolean same(String string){char[] data = string.toCharArray();char[] first =null;char[] second =null;boolean value =false;for(int i =0; i < data.length; i++)if(data[i]==' '){
first =newchar[i];
second =newchar[data.length- i -1];System.arraycopy(data, 0, first, 0, i);System.arraycopy(data, i +1, second, 0, data.length- i -1);if(first.length== second.length)for(int j =0; j < first.length; j++)if(first[j]== second[j])
value =true;else{
value =false;break;}elsereturnfalse;break;}return value;}}
This code does not do much; it accepts two words separated with a space, and returns true/false depending on whether the words are equal or not. This works quite well, but imagine that we are asked to enhance it, so that the comparison is not case sensitive any more. As a first step, have some test cases in order to preserve the original behaviour:
There can be more, but for now, they are enough (check the screencast and the source repository for the whole test suite). TDD and the green bar, among other things, make you/me confident that the upcoming changes won’t affect the already implemented behaviour. This is the most important thing in it for me. Now back to the legacy code. Have the test case, which will drive the change:
After having this test case on board, which turns the bar red, the following will happen:
introducing the change (implementation of the non case sensitive comparison)
massive refactoring (nobody doubts it I assume)
Such a huge work may take “hours” even for that small piece of code above (I admit that in this case it won’t take hours). Obviously, you cannot commit it, cannot ship it to the customer, you cannot show it to others besides your pair, etc. until the change is not done.
Ideas for Solving the Problem
The TDD mantra says red-green-refactor, red-green-refactor,… but is does not say anything about the time spent in each phase, a factor that I consider very important. For example, staying too long in green and refactor means less or no progress, staying too long in red means losing control over the code, causing quality and feature degradation. In order to avoid these cases I’ll take two ideas:
The small steps help to make progress in green, and the limited time in red helps to stay on the right track. If you realize after some minutes that you are doing changing and refactoring in one step, you may do your last step back, and perform only one of them. I prefer doing the refactoring first, because the green code is shippable, shareable, and nevertheless I feel more secure, because the test cases prevent me from introducing bugs in the existing functionality. So a third item to the list:
take a step back: do the refactoring first and the change afterwards
One can argue that only the change has value to the customer so that shall be done first and refactoring afterwards. In my point of view, this statement highly depends on the definition of value. If the customer got the code, the value wouldn’t only mean the functionality but quality as well. However, I admit that there are cases when the change comes first and refactoring afterwards.
Ignoring Test Cases
One way of stepping back is to ignore the new test case. Do not delete it, just @Ignore it:
@Ignore("Introduces new functionality, but I must refactor first")
@Test
publicvoid shouldReturnTrueForTheSameButDifferentlyCasedWords(){
assertTrue(comparator.same("wORd word"));}
An ignored test case is similar to a TODO note, but a bit more effective. You know that the test case is there, because Eclipse shows the number of ignored test cases. Now everything is green again, you spent less time in red, but need a smaller step. Do the refactoring now. The first test cases make sure that nothing is going to be broken, and with wise refactoring, the introduction of the change will be fast and easy.
When the refactoring is done, remove the @Ignore tag, and introduce the change. You can apply this technique as many times as you want.
Don’t get me wrong, there is nothing wrong with such test cases, on the contrary: they are very good test cases. They not only drive the implementation, but also point out that the code base needs refactoring.
Write Test Cases With Temporarily Wrong Assertion
The example above introduces something new, but sometimes an already existing functionality has to be fixed. Imagine that the customer complains that the method does not work if the input contains trailing spaces. Now, the test case looks like this:
@Test
publicvoid shouldReturnTrueInCaseOfSameWordsButWithTrailingSpaces(){
assertTrue(comparator.same("word word "));}
Another way of stepping back is to change the assertion here like this:
@Test
publicvoid shouldReturnTrueInCaseOfSameWordsButWithTrailingSpaces(){
assertFalse(comparator.same("word word "));}
This test case provides more safety, because this is how the legacy code works, isn’t it? As soon as the refactoring is done, do not forget to change back to the original test case.
Example
In the following screencast I demonstrate how to use these techniques while working with the example legacy code.
The source code of the example is available on github.
Firefighting happens usually during bug fixing and at the end of projects, when the project may or may not be as successful as people want it to be. In this particular post, I’m going to talk about the latter case.
Usually, when the project is in trouble, management offers more (human) resources in order to finish the project in time and deliver the committed scope. Based on Brooks’ Law, this is not a wise decision, but it happens all the time.
According to Brook, the amount of new man-power is not directly proportional to the team’s performance (see figure). The possible reasons for performance degradation are:
increased communication
increased coordination effort
unwanted diversity inside the team
bringing new people up to speed
Scrum masters and team leaders are usually reluctant to accept such a change inside their teams for obvious reasons. In most cases they have no other choice but to take the new people into the team and try to do their best. This is a change inside the team, and we have now arrived at Tuckman’s stages of group development:
With the new people on board the team, it does not matter in which stage the team is right now, it goes back to storming stage. Now the team has to deal with
the costs of increased communication and coordination effort
the costs of development to norming and performing stages
the costs of finishing the project in time
This overhead is so huge that failure is guaranteed. Seems to be a lose-lose situation. By rejecting the additional resources, the team is facing a conflict with management, which is time consuming. Again: lose-lose.
If a scrum master or a team leader faces a situation like this, I would say that he should accept the additional resources and form a new team with only one single purpose: to find out what went wrong and what needs to be changed so that the original team can finish the project in time. With this approach, the integrity of the original team won’t be damaged, as opposed to the scenario described above, and the single purpose guarantees that the new team will perform. This task is a creative one, therefore there is no need to motivate the new team with money:
In most cases people do not like changes, so they’ll do everything in order to avoid or minimize them. There are two changes here:
the additional people have to switch teams
the original team has to deal with the new members
Both of the parties are motivated to go back to their original states: source team and original team setup. There is no need for money here as motivator. One can ask how long this new team shall work together. In the agile world – among other things – time boxing is very important. I would say that this new team shall work together on the problem for 2-5 days. One day is not enough for the forming stage and more than a week risks the whole project. Of course, the new setup can work together on the project during this period, but the single purpose (root cause analysis and plan for proceeding) shall be the most important thing for them.
This approach turns the lose-lose situation into a win-win situation; management is happy, because their idea – new resources on board – is implemented, and the team is also happy, because they can stay together after finding the root cause of the problems and finding a way to finish the project.
For firefighting situations I prefer using the Kanban framework. It is very informative, and generally keeps things under control.
If there are a lot of things to do, people tend to do context switching, which makes them less effective than they could be. Kanban limits the number of cards in a particular column, which does not protect developers from context switching. The number says nothing about the individuals, only about the team.
One method I find usable is to have a limited number of Kanban avatars (custom picture on a magnet, a sticker, coloured magnet etc.) for every colleague. If someone is working on a card, he or she must put one of his or her avatars on that particular card. One advantage of this method is that everybody can see who is working on what, and the number of possible context switches is limited. With two avatars, everyone works on maximum two items, and switching between these two is not that difficult to handle as switching between four or five. Finishing one card is still the highest priority.
If, for some reason, one card is blocked, the avatar stays there, and that colleague can put another avatar on another card, if he or she has any left. If not, an already started task shall be finished. If that is blocked as well, then the team shall solve the blocking situation.