Moving from cowboy coding to agile development

Sunday, November 05, 2006

A New Beginning

The first couple of weeks in my new job have been really nice. The atmosphere is really relaxed, yet ambitious and supportive. Also the people have been nice, so I've settled in very well.

Agile methodologies have also found some fertile ground there, before and after my arrival. To my (pleasant) surprise, my second working day included a presentation about JUnit to everyone who was interested. There was also interest in going to the recent Agile Seminar. It was made clear that attending the 3-hour seminar would count as working hours. This was a really good sign, helping me to feel that the company wants to support its workers' learning. There were four or five of us who eventually attended the seminar.

I also had a talk with the colleague that gave the JUnit presentation and asked him to join the next Coding Dojo. Only one or two days later the next dojo invitation was announced, so the timing was perfect. We went there together and talked a little about giving an internal dojo session at our workplace later. I believe the TDD knowledge level is not sufficient yet for a full-blown randori, so we might start with a lecture-like approach.

I'm actually feeling really busy nowadays at work. Not because of the workload (which is actually really decent) but because I'm eager to participate in everything that helps us get our processes better.

Friday, October 06, 2006

Changes

My mission to introduce automated testing at work is now finished. It is far from complete, but today was my last day there. In a couple of days I'll be working for another employer.

It seems I managed to plant a seed of interest in testing; some (now past) colleagues told me they are sorry that I didn't get further in introducing some tests and the testing process. That makes me feel that they have actually understood the importance of testing, but it also suggests that no one feels confident enough to continue the work on their own.

Then again, it's out of my hands now. I guess all I should be interested in is that my new employer is excited about agile development.

Friday, September 01, 2006

Old Habits Are Hard to Break

In the last two weeks I've spent some time writing a small program, which I finished a couple of days ago. It served its purpose but looking back, I see some room for improvement.

First, the GUI was a crucial part of the program, and I am still to learn automated testing techniques for user interfaces, so I didn't go far tests-first. Instead of learning to use the appropriate tools and techniques I decided to go on without (GUI) tests.

Second, since I didn't know how to write good tests for the interface, I started slacking with writing tests altogether. When I was running out of time, I also reverted to the "good" old cowboy coding style.

Third, I tried to write the program one feature at a time, using a simple planning game by myself (me being the customer as well as the software provider). I made some time estimates and decided on the next features to write based on the available time. I guess I could have prioritized the features in a different way, but I did get a working piece of software in the end - with the chosen features done to the end, and the rest missing completely. However, my accuracy concerning the time estimates varied a lot. I also think I could have adjusted my time estimates better based on the time I used while implementing the previous features.

And last, I actually noticed all of these issues already while writing the program but didn't "have time" to correct them. I guess this way of thinking is the first thing I should change. (This might be closely related to reverting back to the old coding style, but deserves to be mentioned separately.)

Still learning, I guess. But then again, aren't we always?

P.S. Two co-workers returning from their holidays commented on my unit testing paper. One of them was impressed by the detailed instructions I had written. The other - the head of our C# development, kind of - asked where I got the idea and started chatting with me about automatic builds - of which I didn't write much in the paper... I guess there might be a breeding ground that I was unaware of for some agility.

Tuesday, August 15, 2006

Work update

Since my last post I have tried using VS2005's testing framework and NUnit with our actual code base. Yesterday I checked in the first tests (totalling 38 test methods for a single class) to our source code control. I had picked the class under test quite randomly but hit a sweet spot with my choice - one of the tests found a bug in a method that was written more than three years ago. And I actually tried to write the tests to match existing code... Perhaps the method is not used very much, since no one had noticed the bug before. (It should have turned up by now, because the method generates a short part of an SQL statement, and with one valid input it returned SQL that is not valid in any DBMS.) There was also at least one method where a null parameter was not considered a special case.

"The other guy" tried checking out the tests and running them, and everything went smoothly. He also thought the tests were a good example of finding small bugs in existing code. Since everything had worked, I updated my paper and added some technical details about running the tests.

Today I finished writing it (at 19 pages) and checked it in. I also wrote tests for another of our basic classes and checked them in as well. This time writing 24 test methods took a lot less time than the previous ones.

So far no one has commented on the work - except for the guy who wrote the method with the bug I found. I went to check if it was ok that I used his method as an example of hard-to-see bugs that could be found easily with testing. He was just browsing through my paper and was pretty impressed. Although no one else has given any feedback, I feel pretty confident. I actually think I'm changing things here.

Let's see if I'm just fooling myself or if my colleagues also start writing some tests.

Friday, August 04, 2006

Winds of Change

Last week I wrote how I had planned to start talking with my colleagues about improving our processes much more than I had did before. That's exactly what I did.

First, I talked with one of the guys in our internal IT support. He totally agreed with me that we should not be doing things manually if they could be automated. He also told me that one of the developers had tried "some XP thingies" earlier, so I went to have a chat with him. He had read something about unit testing but had not done any on his own. I promised him I'd try writing some tests for some of the most used classes in our production code. He was willing to help, if I needed anything.

Then came the development discussion. I told my superior about automated tests and builds, and she gave me a go-ahead to try and write some tests. I installed Visual Studio 2005 that same day and have tried writing some unit tests with the built-in testing framework. So far I've just been playing around but I still feel great. I also wrote an 8-page paper about unit testing, including instructions for getting the framework to work properly. To be honest, the paper ended up having some propaganda-like raving about the importance of testing and proper coding style. It was still received well by the other guy interested in XP, who is the only one who has read it so far. We also thought I might install NUnit and try it out for comparison. Now I'm getting some other work done, but next week it's time to dive into writing those actual tests.

The only issue I'm worried about is our company culture. All the people I have spoken with have been somewhat intrigued by my Agile ranting but have also questioned whether it's possible to change the way people here work - and have worked for over ten years. That's a challenge, and I must try and point out some benefits from writing tests for our code. I'm also curious to see whether our code is actually so well written that it will be fairly easy to write those tests.

As a side note: I even started thinking whether tests could be used to "guard" good coding standards. For example, making sure methods don't change their parameters' state as a side effect could usually be tested quite easily. Then again, including such checks in all unit tests would probably make the test code bloated and tangled. Still, even if people agree with me that such side effects are unwanted, they might have a hard time adapting to a new coding standard that differs from the way they have written code for years now.

Well, I guess (and sincerely hope) I won't find that many serious code smells when digging through the code base.

Wednesday, July 26, 2006

Time to Change

Although I think I have been doing too much reading (both books and blog posts) and too little actual development work, I am happy I kept reading and came across a link in Dave Nicolette's blog. It pointed me to James Shore's Change Diary.

One of the most interesting points was somethings James wrote in the "Years Later..." section of "Week sixteen".


"I have not yet seen technology-first achieve significant organizational impact. If I didn't have management support for structure-first or switchover, I would focus on getting management support. At best, technology-first could provide some ammunition for those discussions, but be careful: some technical practices, such as test-driven development, take a while to show dividends. They slow you down in the meantime. Depending on your situation, they could hurt your cause rather than help."


Since my employer is rather far from being agile, I have planned to start using TDD alone first, not pressuring anyone else to do it before I have something to back up my opinion that it will help us. However, if I understood Mr. Shore correctly, this is probably not an efficient way to achieve things. I should try to change the management's mindset instead.

Felicitously, while I was reading the diary, my closest superior came to settle a time for my annual development discussion session. It's now booked, and I'll be sharing some of my views with her on Friday (in a couple of days, that is).

Now I should get some well-prepared points on paper. I'm thinking of talking about automation issues. As a first step, we could put all of our documents in a version control system, so that they're not scattered on our file server directories. I believe this could keep them better updated. I'm also going to ask our customer support team how they make their builds when preparing installation packages. It could probably be done on a daily (or nightly, to be exact) basis. And most important of all, I'll talk about automated (unit) testing, which I already mentioned a year ago.

Let's see if I can get things rolling, or if I should spend even more of my time browsing job ads (I do that already, but not very aggressively).

Wednesday, June 21, 2006

Team Experience

I have been reading Hunt and Thomas's The Pragmatic Programmer (which I probably don't have to recommend to anyone anymore) and was pretty astonished when I read the section "Pragmatic Teams" in chapter 8. Everything I read seemed to ring a familiar bell somewhere in the back of my head. And every ring could be translated to "been there, done that"; I was part of a team like that once.

There were four (and a half) of us. We joined the company one by one and worked separately at first. Later, when the company hired more programmers, we became one of two teams working on the company's two (partly overlapping) products. Unofficially, we were the "senior" team, since all of us had worked in the company longer than anyone in the other team. (Nowadays I would say such division is probably not the way to go...) But to many of us, this was the first (IT-related) job and our skills were based only on academic studies and personal spare-time projects.

We still managed to triumph. (When I say "we", I mainly mean the others. Although I did my share of some improvements, I was more like an innocent bystander compared to the others.) Perhaps the biggest reason for this was that things were in a horrible state. There was no source code control system. There were no builds (and I am not talking about automated builds here, but builds in general). There was no way of knowing which version of the product each client had. (Well, since there were no versions, this was quite understandable.) There was no documentation whatsoever on the database tables the products used. I guess nobody liked it that way. Things had just happened when there were a couple of people writing code as fast as they could, and soon most of the work was putting out small fires everywhere. However, even the bugs that were found were not reported anywhere.

The team managed to change much of this. We started using source code control. We started writing small descriptions of database tables we added or modified, and put those in a common binder. (It would have been better to use plain text documents and store them in the version control system, but at least we did something...) We built a small intranet that included a bug-reporting service and a bug database. The customer service people could add bug reports and follow the fixing process as we assigned each bug to one of us and kept track of its status. Using the version control, we started making builds and keeping track of the versions each customer had. (We also made a proper installation media for the software but I think it was mainly done in the other team.)

We also did things that were not that obvious but which Hunt and Thomas mention in their book (and their book was out by then, so this probably wasn't a coincidence - even though I personally didn't read it back then). We came up with a name for the team and used it in all of our documents, and pretty soon the rest of the organization started using the name, too. We shared knowledge by making small handouts of books we had read and sharing them with other members of the team and everyone else in the company who was interested. We added a page in the intranet where people could write short reviews of new books. We even had names for the builds we were making (thanks to Pekka and his imagination :) ). And we sat together in one small corner of the company office, talking out loud when we faced problems.

Even though we all eventually left the company to find new (and probably not so many) challenges, I can truly say we all learned a lot. And while doing it, we had some serious fun. I can't say I miss working there in whole. But I definitely miss working as a part of that team. *sob*

Monday, April 24, 2006

Abusing TDD genetically

I recently made a small program as an example of a genetic algorithm. It was a minor school assignment, so the algorithm type to use was set in advance. Trying to use TDD as much as I can nowadays, I once again started writing code tests first.

After learning about how tests should drive design earlier, I now faced another interesting situation. Part of the design (the algorithm type bit) was already set, so the tests should actually adapt to that. I thought about how I should write tests in a case like this, and decided to experiment a little. Instead of writing tests for existing code (that didn't actually exist yet, but was quite thoroughly planned and only waited to be written), I tried to come up with tests that could have forced me to choose the algorithm I had already thought about. I know this was not anything like the approach TDD is meant to motivate, but it was fun - and writing unit tests still really paid off.

I managed to write tests that drove me towards the algorithm I had already partly designed and made the original design, in a sense, justified. Writing really demanding tests also made me improve the original design by showing that some details in the algorithm worked only occasionally as I had meant them to work. "Occasionally" is an important thing here, since genetic algorithms form a partly random approach to finding solutions in large search spaces.

Next I will present some examples of how unit testing helped me while writing a simple genetic algorithm. If you're not familiar with the concept, you might want to quickly browse Wikipedia's article on the subject. If you want to skip that, let's just say the algorithm is all about evolution (of solutions to the problem) and leave it at that. :)

Uncle Bob's blog post Extract Class provided me with a good way to test randomness. For example, I implemented the mutation of individuals so that the likelihood of mutation depended on the fitness of the individual: a perfect individual should never mutate, a strong individual should mutate only with a small likelihood, and a very poor individual should almost always mutate. So, I wrote tests for each case, writing a loop in which I called the mutation control method and counting the number of times a mutation actually happened.


public void testStrongIndividualVeryUnlikelyMutates() {
Individual original = new Individual("88888");
Individual after;
int changeCount = 0;
for(int i=0; i<1000; i++) {
after = new Individual("88888");
after.mutateIfBadFitness();
if(!original.equals(after)) {
changeCount++;
}
}
assertTrue((changeCount < 250) && (changeCount > 50));
}



One of the things unit tests helped me improve was generating offspring for two parent individuals. Since I didn't want the individuals to produce exact copies of themselves (since that would not give me any new information), I wrote a test that ensured the offspring was different from both parents. I was surprised to see the test fail with randomly generated individuals, since I thought a good selection of the crossover point alone would solve this. I soon realised that if the parents had identical "DNAs" on one side of the crossover point (e.g. "123|456" and "888|456"), the offspring would be an exact copy of one the parents. This was really easy to solve by forcing the offspring to mutate at birth, but I might not have found the flaw without a good test.

All in all, the task was fun and educating, although my approach probably added a (big) twist of solid coding horror to TDD.

Wednesday, March 22, 2006

Doing What I Love

While doing some study-related research, I stumbled upon a nice article on doing what one loves by Paul Graham:
http://www.paulgraham.com/love.html

Although I might disagree with one or two particular things, I think Graham has some excellent points. Especially his musings on people's motivations sound really familiar and accurate. They reminded me of Terry Pratchett's words: "Too many people want to have written." Luckily the hacker culture probably isn't as tempting as writing novels, and "Too many people want to have written killer apps" isn't excruciatingly true. At least, not yet.

P.S. If someone has read Graham's book Hackers & Painters, let me know what you think about it.

Monday, March 20, 2006

My Agile Week

Last week was probably more agile for me than any other week has ever been. On Wednesday I participated in my third randori coding dojo, and on Friday I eagerly listened to each of the three presentations in the agile seminar at Kiasma.

The seminar was absolutely great. The first presentation was Vasco Duarte's Team Building and Agile Software Development, followed by Sebastian Nykopp's Test Automation in Agile Development Today and - last but definitely not least - Joseph Pelrine's How Agile Works: Complexity and Agility. Vasco Duarte and Joseph Pelrine talked more about the social and "soft" side of software development. Some of the stuff sounded pretty familiar to me because of my leadership and management studies, but there were also several new things and the familiar ones were now tied to my own trade better than ever before.

Sebastian Nykopp's presentation was more on the technical side, and thus perhaps a bit more challenging to listen to. However, having learned TDD so far only at grass-roots level, it was really nice to see the whole bundle of test automation from FitNesse to the (familiar) JUnit unit tests. As I wrote in my last post, I really should get a grip of TDD on a larger scale, so this presentation really hit a good spot.

I think the presentations gave me some valuable ideas on the ways I could try to get TDD (and perhaps some other XP methods, later) started at my workplace. We'll see.

The dojo on Wednesday was - again - loads of fun. This time the dojo was held at the FifthElement premises, being the first dojo outside of the Reaktor office I have participated in. Although I consider myself as a novice in TDD, I actually started feeling the agony of being on the red bar too long... We stayed "on the red" for over five minutes during some big change, and although (or because of) I wasn't at the keyboard at the time, I almost started feeling uneasy physically, waiting for the green bar to appear. :)

Joseph Pelrine said on Friday "[After RUP,] Agile was like a homecoming for me". I can't say I feel the same, but there's definitely something in the agile ways that I find more and more alluring. I'm already feeling TDD puritanism creeping in, and I really like the feeling.

Thursday, March 09, 2006

All design is not BDUF

I don't use TDD at work. So, the stumbling steps I take on the test-driven path are ones I choose voluntarily while doing (solo) school projects etc. The Coding Dojos of Agile Finland as well as a few skilled friends have helped a great deal but I feel my advancement is still rather haphazard. I also must confess that I have not read even the most basic books on extreme programming. So, many of the things I trouble myself with might be rather evident if I had. Therefore I would be more than happy to hear any recommendations, so I could stop asking stupid questions. (Although I know I won't have time to read anything "extra" in months...)

One of the big things I have stumbled with is writing good tests. This is of course one of the cornerstones of TDD and thus an important thing to learn well.

For me, I think the biggest question in writing tests has been: "Where do good tests come from?" As far as I have understood, the production code should be written to pass the tests. So, writing code after a test is defined should be quite straightforward. And this has definitely been the case with simple examples, e.g. the topics we have encountered in the dojos. While writing entire functional programs I have, however, been a bit unsure what to do and when to do it. Good production code comes from good tests, but where do good tests come from?

I have lately been writing two programs in which I have tried to use TDD. The one I'm just finishing is a naive parser for reading Bayesian networks in Hugin lite form and constructing actual network based on the information read. (Actually, that's only the first part of the whole program, but this is a group effort and we're working independently on different parts of the program - and this part was my responsibility.) Writing tests was quite straightforward; I had the grammar of the input files and could write tests to check the validity of small parts of the input file one at a time. I guess combining this part of the program with the other parts would have been more challenging test-wise, but I am the only one in our group who writes unit tests, so I avoid the challenge here (sadly, I might add).

The second program is a sudoku puzzle solver (yes, I know there are a lot of them already, but this was a good chance to try some AI-related stuff on my own), and here I ran into problems. I started with the smallest thing I could think of: a cell on the sudoku game board. The first tests were really easy to think of; building a constraint-based solving algorithm/strategy would definitely require the cells to know which values were still available to them, so I started by writing tests that removed some values from a cell's list of possible values and checked the remaining values to be valid etc. Soon it was clear that if a cell had all but one of its possible values removed, the remaining possible value was the only valid value that the cell could have, so it should automatically be chosen. Testing and implementing this was easy.

After a while things got hairy. When adding the bigger components of the program, I started writing tests for some particular kind of data constructs I thought I might need. I chose to write tests that would suit the faint idea I had about the inner implementation of the program. Pretty soon I realized I had no idea what I was going to do next. I had a bunch of tests and methods that passed those tests - and I didn't know if I was ever going to need the functionality I had just implemented. Trying to avoid Big Design Up Front, I almost had not designed at all. I had code that would probably remain dead even as the program would get finished.

I think the problem I have has two sides. First, I lack the knowledge and experience to decide what kind of functionality should be tested. For example, should I test some basic file handling routines? (I guess as long as I use the basic components offered by the programming language itself this would be overkill. However, custom error handling and other special functionality probably should be tested.) Second (and I think more importantly), I don't think enough about the big picture; what is unit testing (and agility in general) trying to achieve and why does TDD help to make things agile? Timo Rantalaiho wrote a good reminder in his latest post: "[W]hen doing TDD, we're not supposed to write tests but to specify requirements."

One of my teachers once said that if written academic text isn't clear, the thought behind it was not clear either. I think the same thing applies here: bad code is a sign of bad thinking, and writing code (or a test) when you're not sure what it should do means you're pretty much writing worthless crap. Avoiding BDUF should not mean avoiding design entirely.

Wednesday, February 15, 2006

Another Setback

Once again it seems I won't be coding much on my free time in a long time. My plans suffered a serious setback when I found out that the research group I wanted to join isn't hiring at the moment. So, I guess I have to find someone else who is willing to pay me for doing my master's thesis, or the latter half of the year will be a difficult balancing act between work, studies and writing my thesis. That would not leave time for doing any TDD (unless I start doing some serious propaganda at my current workplace and get to use it here).

Friday, February 10, 2006

On Test Coverage

On Wednesday I spent three hours at Reaktor again, participating in an Agile Finland's coding dojo session for the second time ever. It was fun, and this time I had a particularly good lesson about (lacking) test coverage.

First, a small disclaimer: I don't mean to reprove anyone involved in writing the code that we started with. I can't claim I would have noticed this bug during the previous session myself, if I had happened to be there. I read the code base through before the Wednesday's session and this didn't catch my eye.

Now, to the point. The challenge was to write a poker hand evaluator, and for this, we need a way to construct the hand in some way. This is the corresponding method as it was:


private Card[] createHand(String hand) {
String[] cardStrings = hand.split("-");
List cards = new ArrayList();
for (String cardString : cardStrings) {
cards.add(new Card(cardString.substring(0, 1),
cardString.substring(1, 2)));
}
return cards.toArray(new Card[cards.size()]);
}



This method takes a string representation of a five-card hand, e.g. "DA-D2-D3-D5-D4" (which would be a straight flush from the ace of diamonds to the five of diamonds). The method generates the corresponding Card objects with a suit and a rank. However, there's a small error. A card with a rank of 10 can't be generated, since the latter substring only takes one character to represent rank (and elsewhere in the program the rank was supposed to be "10" - this is also the only card in which the error occurs, since aces are represented as "A":s, and facecards are "J", "Q" and "K", respectively).

The fix was very simple:


...
cards.add(new Card(cardString.substring(0, 1),
cardString.substring(1)));
...

This error had not caught anyone's eye - maybe because there was not a single test including a card with the rank of 10. To me, this raises a question about the right kind of test coverage to use with TDD. Should there have been test cases with every single card tested? Probably not. But this time taking something - even this simple - for granted resulted in a bug.

Personally, I think one of the points of TDD is not to think you have taken everything into account, but knowing you have done so. (Well, of course programs can never be completely bug-free, but you know what I mean...) To a non-expert coder like me, I guess it's exactly the small things like this that I can learn to use extensive unit tests with. Live and learn - sometimes the hard way.

Thursday, January 26, 2006

A Wicked Programming Language

Sometimes I find "geeky", cryptic coding funny and even interesting. Today I stumbled upon a "Turing-complete programming language" called Brainfuck (see also the corresponding Wikipedia page).

Of course code (or a language) like that is merely entertaining. I would never like to see anything like that in a production code I might have to maintain. (And, pointing to my previous post, I would never advise a novice programmer to see anything like that for learning purposes.)

Still, the language definition put a smile on my face.

Wednesday, January 25, 2006

FP vs. OOP?

Because my computer is still being fixed (luckily the repair seems to be covered by the guarantee) - and I can come up with a bunch of similar excuses (like my mail server blocking the invitation to the second/third coding dojo session in Helsinki) - I have concentrated on reading articles and blog entries on programming.

One of the most interesting topics I ran into was the issue of teaching programming to new students. There seem to be two quite strong schools: one in favour of FP (functional programming) as the first thing to teach, and one in favour of OOP (object-oriented programming) in the same role. One of the most interesting articles or publications on the subject was Matthias Felleisen's presentation How to Design Class Hierarchies (pdf, 95 pages - but reading the whole thing won't take more than 10 minutes).

Felleisen raises some interesting points - at least interesting to me, since I started my "academic" programming life with Java.