A TDD success story

August 1st, 2010

In a month or so, my co-worker and I will be wrapping up the project that we have been working on for the last 15 months. It’s a website for a company in the construction industry that bids on jobs and then tracks the progress of the jobs, purchase orders, billing, and everything else they need to run their business. We used ASP.NET MVC, Fluent NHibernate, AutoMapper, and SQL Server 2008.

We practiced test-driven development from day one. We wrote tests for our .NET code, tests for our JavaScript code, and tests for our SQL code. Right now we have over 14,000 tests, and I think we can break the 15,000 test barrier in the next month. I can run them all in about 5 minutes.

I cannot tell you how invaluable these tests have been. First of all, our application deals with money. The users will input a bunch of data about a job and then our application will tell them how much to bid on the job. We cannot afford to have bugs in our code that would miscalculate the amount to bid on a job, because that would lead either to over-bidding (in which case they would win very few) or underbidding (in which case they would win jobs and take a big hit). It just has to work.

Second, we did not have a QA team on this project, it was just the two of us developers. Frankly, I don’t have time to go back and manually test stuff in the app or regression test it when we need to make a change and deploy something. We do have bugs from time to time, but we haven’t had any critical bugs.

Because of our tests, we have been able to get as close to continuous deployment as I would feel comfortable with. On average, we deploy 2-3 times a week. When we go to do a deployment, I usually go through the site and manually test the new features that we are about to release. If I don’t find any problems, I run our deployment (which is all automated), and 5 minutes later, the changes have been deployed. I never go back and regression test old features or stuff in areas that we didn’t change.

Since the whole test and deploy process takes only about 15 minutes, users get their changes quickly. They don’t have to wait until later in the week, or until our next scheduled release. We have reduced the cost of change to pretty much just the time that it takes to code the changes.

All of this is possible because we were diligent about test-driven development and writing good unit tests for everything. The reason that we don’t spend time regression testing is that we don’t expect anything to be broken, and it very rarely ever is. This means that we can spend more time delivering business value and less time ensuring that we didn’t break something we wrote a year ago.

I’m not writing this to say how awesome we are as developers, because anyone can write tests and have the same kind of success. Also, we could’ve cheated and not written tests and ended up with potentially costly bugs and more time spent regression testing.

This is why I practice test-driven development. You end up with well-designed code, you drastically reduce bug counts, you can release more often, your codebase stays under control, and you have a lot less stress.

Reducing the cost of change

July 30th, 2010

If there’s one constant in software development, it is change. Our entire workday is spent changing things and responding to change.

Every time you write a line of code, you are changing it. You might add new features, fix bugs, or make changes to existing code. However you put it, you’ll be changing the code.

Requirements will change. You often find this out after you’ve coded a feature, and now the business either changes their mind or you find out that you made some wrong assumptions.

Business priorities will change. What was important yesterday may not be important today, and six months from now, all kinds of things might change. There could be new, more important projects on the horizon, new people in charge, or new business opportunities.

Software development teams change too. You might stay at your company for many years, but during that time, you might get moved from project to project. Developers will leave the company, and others will join. The end result is that many different people will end up working on the code that you’re writing today.

Since we know that we are going to have a lot of change, it would therefore make sense to reduce the cost of change so that we can respond to change as quickly and easily as possible. So how can we reduce the cost of change?

Automated Testing

Every time I add a line of code, I risk breaking my application. Breaking the application is just not acceptable. We’ve come to accept bugs as a part of the process, but that doesn’t mean that it’s OK to write bugs. If you’re not striving to write bug-free code, then maybe you need to raise your standards.

Nevertheless, the fact is that we all screw up and we all have the capability to create bugs. Knowing that, I want a way that I can easily test my application so that I can ensure that I find out if I broke something once I change it.

On my current project, I’m writing an application that helps my client bid on construction projects. They can’t afford to have bugs in their system that will cause them to incorrectly bid on these jobs or they could be out a lot of money. The bottom line is that it has to work because their entire business is now running on the application that I’m writing. They also come to me on a regular basis with new feature requests and things they need changed. They don’t want to wait months to get this functionality, and they also don’t want me to break any existing code.

Thankfully I have a large suite of automated tests that I can run to ensure that I didn’t break anything. This isn’t foolproof and things sneak through, but it’s usually little stuff that isn’t critical. But it’s good enough proof for me that I didn’t break the existing functionality. As a result, I can release to production daily and I spend virtually no time manually regression testing my application when I deploy. My unit test suite has reduced my cost of change to pretty much just the time it takes me to code the changes, so now I can react quickly to changes, release all the time, and I can do it without breaking existing code.

Clean Code

Sometimes people will write their unit tests, write the code to make the tests pass, and then assume that that is good enough. That’s great that you wrote tests, but how easy is it for someone to make a change in your code?

I feel that clean, readable code is very underrated these days. When you write code, are you thinking of the person that is going to need to understand what you did once you’re not around anymore? That scenario is very likely to play out at some point. So when you write code, create methods, variables, and classes that are very descriptive. Place a priority in making your code easy for someone else to read.

“Clever code” is the opposite of clean code. I’m talking about using complex algorithms to solve simple problems, trying to using a complicated LINQ statement to do an operation that would be more readable in a foreach loop, or trying to implement something in the fewest lines of code as possible. There are times where you might want to do things like this, but usually it’s more important to write code that is easier for someone else to read and understand. Again, remember that someday someone else is going to have to change your code, so write descriptive code that makes it easier for that other someone to know what to do.

Tests Are Documentation

Your unit tests are documentation of what your code is supposed to do. I practice behavior driven development, which means that my unit test methods describe some business functionality that my code is in charge of executing. This way, if someone needs to know what my code does, they can look at the test classes and methods and see what it is supposed to do.

Lately I’ve started adding a lot more comments in my test methods. The class names and methods names may describe what the code is supposed to do, but it doesn’t always explain why. I’ve found that sometimes it helps to write down the why because that is also important information. Since people can look at my tests as documentation of what my code does, I figured that this would be the best place to put the whys, because if someone has to change my tests, they’ll immediately know why I did what I did and they’ll have a reminder that they need to update my comment.

Build Iteratively

Since business priorities and requirements are going to change, we want to reduce the cost of this change too. If the business comes to you and drastically changes the requirements or certain assumptions that you might’ve made, it might be really costly and painful to change, and you can only do so much about that. What we can do is make sure that we’re constantly checking back with the business to see what they want now so that we have as few surprises and as little rework as possible.

This is one reason why Agile projects often run on iterations. As a part of this process, meet with your business sponsor every week or two and talk about what you’re going to do next. Show them what you’ve done in the last week or two, and make sure you’re on the right track. This way, if they’re going to change their minds, you’re giving them permission to do so and you can adjust what you’re doing as soon as possible so that you have the least amount of rework.

Always Be Aware of Change

Quit thinking short-term and quit shoving code in just to get it done as fast as you can. You may think you’ve done something good by getting it done faster, but if you create more work and pain in the future, it’s a net loss. Instead, always think about what the effects of what you are doing now will be two years from now. Your goal is to create business value without creating technical debt. A home builder can do shoddy construction and put nice looking paint and siding on the house, but 10 years from now when the problems arise, people won’t be thinking very highly of that builder. Don’t make the same mistake. Take pride in your work and strive to always leave your code base cleaner than you found it.

Discounted rates!

May 5th, 2010

Sign up for TDD Boot Camp at the discounted rate of $750! Also, if you sign up with a group of 3 or more, you get the even more discounted rate of $600.

The bottom line is that I want to help you learn TDD and get all the benefits out of it that I get on a daily basis.

How to use Rhino Mocks – documented through tests

March 12th, 2010

I wanted to come up with a way to show people how to use Rhino Mocks (other than telling them to read the documentation). What better way to do this than by showing you how it works through a bunch of simple unit tests that document how Rhino Mocks works?

So that’s what I did. You can view the code here, or if you want to download the whole project and run the tests, you can get the whole thing here.

A response to the SWE101 attendees who tried to solve the TDD problem without TDD

March 2nd, 2010

This weekend we did some live TDD at the Software Engineering 101 event and broadcast it over LiveMeeting, which was a lot of fun. However, some people have posted solutions (here and here – see first comment) on how they solved the Greed scoring problem without using TDD.

I think you missed the point. The point was to help you learn the TDD thought process and how to put TDD into practice. There are lots of benefits to TDD, and I’ll use these non-TDD examples to illustrate.

How do you know your code works?

Those of you who solved the problem without TDD probably did some sort of manual testing in order to prove that your code is working. You could probably do that with a simple example like the one we had (there were only a few scoring rules). But what happens as we add rules (and in real life we’re always adding rules) and you have to manually test 16 rules? For the record, I took the solution posted here and ran my tests against it.

failing test

Tests are documentation

Tests document what the code is supposed to do. Since we wrote our test methods as sentences that read like English, you can figure out the rules just from reading our tests (as you can see in the image above). This is not documentation in Word format which becomes stale, this is living, breathing executable documentation of what the code is supposed to do.

Readability is important

I feel that the solution that we ended up with was very readable. Here is our implementation code:


public class GreedScorer
{
    public double Score(params Die[] dice)
    {
        var score = 0;

        score += ScoreASetOfThreeOnes(dice);
        score += ScoreASetOfThreeTwos(dice);
        score += ScoreASetOfThreeThrees(dice);
        score += ScoreASetOfThreeFours(dice);
        score += ScoreASetOfThreeFives(dice);
        score += ScoreASetOfThreeSixes(dice);
        score += ScoreEachOneThatIsNotAPartOfASetOfThree(dice);
        score += ScoreEachFiveThatIsNotAPartOfASetOfThree(dice);
        return score;
    }

    private int ScoreASetOfThreeOnes(Die[] dice)
    {
        if (dice.Count(die => die.Value == 1) >= 3)
            return 1000;
        return 0;
    }

    private int ScoreASetOfThreeTwos(Die[] dice)
    {
        if (dice.Count(die => die.Value == 2) >= 3)
            return 200;
        return 0;
    }

    private int ScoreASetOfThreeThrees(Die[] dice)
    {
        if (dice.Count(die => die.Value == 3) >= 3)
            return 300;
        return 0;
    }

    private int ScoreASetOfThreeFours(Die[] dice)
    {
        if (dice.Count(die => die.Value == 4) >= 3)
            return 400;
        return 0;
    }

    private int ScoreASetOfThreeFives(Die[] dice)
    {
        if (dice.Count(die => die.Value == 5) >= 3)
            return 500;
        return 0;
    }

    private int ScoreASetOfThreeSixes(Die[] dice)
    {
        if (dice.Count(die => die.Value == 6) >= 3)
            return 600;
        return 0;
    }

    private int ScoreEachOneThatIsNotAPartOfASetOfThree(Die[] dice)
    {
        if (dice.Count(die => die.Value == 1) < 3)
            return (dice.Count(die => die.Value == 1) * 100);
        if (dice.Count(die => die.Value == 1) > 3)
            return ((dice.Count(die => die.Value == 1) - 3) * 100);

        return 0;
    }

    private int ScoreEachFiveThatIsNotAPartOfASetOfThree(Die[] dice)
    {
        if (dice.Count(die => die.Value == 5) < 3)
            return (dice.Count(die => die.Value == 5) * 50);
        if (dice.Count(die => die.Value == 5) > 3)
            return ((dice.Count(die => die.Value == 5) - 3) * 50);

        return 0;
    }
}

Look how readable our code is. Read the score method. Notice how it tells you exactly what it’s doing. Contrast that with one of the other solutions:


static int Score(int[] numbers)
{
    int valueToReturn = 0;

    for (int numberIndex = 0; numberIndex < numbers.Length; numberIndex++)
    {
        switch (numbers[numberIndex])
        {
            case 1:
                if (
                    (numberIndex + 1 < numbers.Length && numbers[numberIndex + 1] == 1) &&
                    (numberIndex + 2 < numbers.Length && numbers[numberIndex + 2] == 1)
                   )
                {
                    valueToReturn += 1000;
                    numberIndex += 2;
                }
                else
                {
                    valueToReturn += 100;
                }
                break;

            case 5:
                if (
                    (numberIndex + 1 < numbers.Length && numbers[numberIndex + 1] == 5) &&
                    (numberIndex + 2 < numbers.Length && numbers[numberIndex + 2] == 5)
                   )
                {
                    valueToReturn += 500;
                    numberIndex += 2;
                }
                else
                {
                    valueToReturn += 50;
                }
                break;

            default:
                if (
                    (numberIndex + 1 < numbers.Length && numbers[numberIndex + 1] == numbers[numberIndex]) &&
                    (numberIndex + 2 < numbers.Length && numbers[numberIndex + 2] == numbers[numberIndex])
                   )
                {
                    valueToReturn += 100 * numbers[numberIndex];
                    numberIndex += 2;
                }
                break;

        }
    }

    return valueToReturn;
}

To me, this code is not very readable. If you had to implement a new rule in this method, it would be hard to do (plus you have no tests to tell you that you broke an existing rule).

The point of this exercise was not to find a clever way to solve a brain teaser. If you want to do a brain teaser, try and write code that will output a Fibonacci sequence using one LINQ expression. That’s the kind of geek stuff that you might do at night for fun. But when you’re writing real code, we care about things like readability and maintainability. When you’re writing implementation code, use common language and write methods whose names tell you what they do.

TDD leads to well-designed code

As we were writing our first test, our test told us that we needed some class that would score the Greed game. So we named it GreedScorer, which does exactly what the name intends. This class will most likely end up following the single responsibility principle because we gave it a very specific name.

But you guys took so long!

We weren’t trying to finish as fast as possible. We were not doing a coding competition. We were practicing and learning the TDD mindset and trying to explain things as we went. As you get better at TDD, it becomes more natural and you learn tricks that help you go faster (for example, sometimes writing a whole bunch of tests, watching them all fail, then making them all go pass is faster than just writing one test at a time and making them pass one at a time).

The bottom line is that we came out with some good, readable, maintainable code (with props to Sirena who helped write it). We were able to prove that our code is working. We don’t have any code that we don’t need. We could easily respond to change and implementing new scoring rules would be pretty easy. Implementing the Score method was fairly easy because we were building it incrementally (solving lots of little problems is easier than trying to solve a big problem all at once). This is why we do TDD!

The business value of test-driven development

January 27th, 2010

Most businesses are creating software for one primary reason — to make money. In order to make money, we need software that meets the needs of the business and can be developed and maintained in a reasonable amount of time with a high level of quality. Test-driven development is a discipline that will help you achieve these goals.

Test-driven development involves writing automated unit tests to prove that code is working. The test code is written before the implementation code is written. By writing the tests first, you will know when the code is working once the tests pass. Test names are written as sentences in plain English so that the tests describe what the code is supposed to do. Over time, you will end up with a large suite of automated tests which you can run in a short amount of time. These tests will prove to you that your code is working and will continue to work as you modify or refactor the code base.

Most software applications are intended to be used for many years, and throughout most of their existence, someone will be changing them. The total cost of ownership of an application goes far beyond the cost of the initial effort to create the initial version of the software. The first release is the easy part — you can build the application from the ground up, you don’t have many hindrances, and developers feel very productive. But as time goes on, productivity tends to decrease due to complexity, developer turnover, poor software design, and any number of other reasons. This is where software development really becomes expensive. So much focus is placed on the original cost of building an application without considering how the original development effort will affect the cost of maintaining that application over several years.

Test-driven development can reduce the total cost of ownership of an application. New developers on the team will be able to change the code without fear of breaking something important. The application will have fewer defects (and far fewer major defects), reducing the need for manual QA testing. Your code will be self-documenting because the tests will describe the intended behavior of the code. All of this leads to flexible, maintainable software that can be changed in less time with higher quality.

Software is intended to deliver business value, and test-driven development will help you write higher quality, more maintainable software that can be changed at the fast pace of the business. Test-driven development will lead to successful software projects and enable you to write software that will withstand the test of time.

Make TDD your meal ticket in 2010

January 27th, 2010

It’s almost the time of the year where people start making new year’s resolutions and setting goals for the upcoming year. Allow me to propose something for your list: make TDD your meal ticket in 2010.

Why do I say this? Because TDD has revolutionized the way I develop software and has helped me write flexible and maintainable software with fewer defects and is a less stressful way to get things done. I can’t imagine ever going back to writing code without tests, and as a result it’s really difficult to work with developers who don’t write tests because they will write code that is hard to test, which means I won’t be able to write tests either.

In today’s economy, it’s important to differentiate yourself from the crowd if you’re looking for a job, hoping to get a raise, trying to get a promotion, etc. TDD can be that thing that differentiates you from everyone else out there.

The cost of unit testing

January 27th, 2010

One question that I hear from people who are new to TDD or writing tests is, “How much longer is my feature going to take if I write tests?” This is a valid question. We all have deadlines, so if we have to add something extra to our development process, it better be worth it.

I don’t think I could say it better than this post did, so I’ll just let you read it for yourself.

Software development is a series of translations

January 27th, 2010

A lot happens from the time that a business owner envisions an idea in their mind and the time that the idea becomes software. The trick is getting through the whole software development process without losing the original idea.

Have you ever played that party game when you go around the circle whispering the same phrase and when you get to the end the phrase is completely different than when it started? Software development ends up like that a lot (especially when the business owner is changing the original idea!).

The problem is that we all speak a different languages. Developers speak one language, BAs another, PMs another, DBAs another, users another, business people another, executives another, and so on. So a large part of our job is learning how to translate what these people want into developer language (code), and doing it so that, in the end, the software speaks to them in their language.

Here’s a simple example:

Executive: “When we hire new employees, we need to make sure that they have a computer ready for them when they start.”

Business analyst: “User will enter the number of new employees on the screen. The system will check the inventory and make sure that we have enough machines, monitors, keyboards, and mice for the new employees. Each employee should have two monitors.”

Now it’s our turn. As developers, we have to do several translations in the process of writing the software. The DBA (or you) may have to design the database schema. You (the developer) have to write the code.

Remember, the goal is to not lose the original idea. That means that each time we “translate” the original idea, we need to do it little bits at a time.

Usually business analysts don’t give you specs that are written exactly how you want them. This is not a knock on BAs, but they speak a different language than us developers. What we really want from them is a set of acceptance criteria. In other words, we need to know what we have to do in order to complete the feature. Not only that, we need to know how we are going to test our feature so that we know that it’s working.

So let’s take the business analyst’s specs and translate them into acceptance criteria:

Given a stash of unused hardware
   When a user enters the number of new employees
      Then it should verify that we have one machine for each new employee
      Then it should verify that we have two monitors for each new employee
      Then it should verify that we have one keyboard for each new employee
      Then it should verify that we have one mouse for each new employee

   When we don’t have enough machines for new employees
      Then we need to order new machines so that we have one for each new employee

   When we don’t have enough monitors for new employees
      Then we need to order new monitors so that we have two for each new employee

   When we don’t have enough keyboards for new employees
      Then we need to order new keyboards so that we have one for each new employee

   When we don’t have enough mice for new employees
      Then we need to order new mice so that we have one for each new employee

We’re not saying anything drastically different from what the business analyst said. But the way that we wrote it is important. Notice the use of the words given, when, then. Here’s the next translation:


public class Given_a_stash_of_unused_hardware
{
}

public class When_a_user_enters_the_number_of_new_employees 
    : Given_a_stash_of_unused_hardware
{
    [Test]
    public void Then_it_should_verify_that_we_have_one_machine_for_each_new_employee() 
    {
    }

    [Test]
    public void Then_it_should_verify_that_we_have_two_monitors_for_each_new_employee() 
    {
    }

    [Test]
    public void Then_it_should_verify_that_we_have_one_keyboard_for_each_new_employee() 
    {
    }

    [Test]
    public void Then_it_should_verify_that_we_have_one_mouse_for_each_new_employee() 
    {
    }
}

public class When_we_don't_have_enough_machines_for_new_employees 
    : Given_a_stash_of_unused_hardware
{
    [Test]
    public void Then_we_need_to_order_new_machines_so_that_we_have_one_for_each_new_employee() 
    {
    }
}

public class When_we_don't_have_enough_monitors_for_new_employees 
    : Given_a_stash_of_unused_hardware
{
    [Test]
    public void Then_we_need_to_order_new_monitors_so_that_we_have_two_for_each_new_employee() 
    {
    }
}

public class When_we_don't_have_enough_keyboards_for_new_employees 
    : Given_a_stash_of_unused_hardware
{
    [Test]
    public void Then_we_need_to_order_new_keyboards_so_that_we_have_one_for_each_new_employee() 
    {
    }
}

public class When_we_don't_have_enough_mice_for_new_employees 
    : Given_a_stash_of_unused_hardware
{
    [Test]
    public void Then_we_need_to_order_new_mice_so_that_we_have_one_for_each_new_employee() 
    {
    }
}

This is how you do behavior driven development. We took our acceptance criteria and wrote them out as code in the form of unit tests. These unit tests will prove that our code is working (when we get to that point) and it will also act as our documentation of what the code is supposed to do.

The reason that this is important is that we’re still in the middle of doing our translation. A lot of people take the tech specs and immediately start writing implementation code. But by doing that, you skip a step in the translation, and when you do that, you risk losing some of the original intent of the feature. This is one of the reason why writing tests before you write implementation code is important. First of all, if you write your tests first, then when they’re passing, you know that you’re done. Second, you’ve written out what the feature is supposed to do — nothing more, nothing less. This is going to help us implement the feature without losing the original intent of the person who thought it up.

Now, yes now, you can go write the implementation code. By focusing on what the end product should be and since you wrote your tests first, this part should be easy. It’s much easier to achieve your goal when you know what the goal is!

What should you learn next?

January 27th, 2010

Most of us at some point have decided that we want to learn some new technology. The question is what you should dive into.

When I interview people, I always ask them about new things that they have been learning, and a lot of people these days are looking into things like WPF and Silverlight. But this is not what I would pick if I were you.

If you were to ask me, instead of learning some new technology, every developer should try to become experts in software design patterns and principles and practices that will help you become a better developer with the technologies that you already know. Our industry has a much bigger need for developers that write well-designed, loosely coupled, well-tested code than we need for people with a basic knowledge of WPF or Silverlight.

Look, there’s nothing wrong with learning WPF and Silverlight, and you can make some awesome looking apps with them. But if you can learn software design patterns and practices, those will help you when using any language for the rest of your career. They will help you write less bugs, they will help you get stuff done faster, and they will help you write flexible code that can easily be changed. Who doesn’t need more of that?

Like I said, I interview people. If I interview you and you know and practice things like test-driven development, the SOLID principles, what the Law of Demeter is, and why all of this matters, then you are most likely in (as long as there isn’t something else seriously wrong with you). In my opinion (and this is just my opinion), if you want to be called a “senior developer” I would expect you to know all of these things. This is much more important than how many years of experience you have.

This may require some research and leg work on your part. I say this because in my opinion, Microsoft doesn’t not actively promote this stuff. Sure, you might find some MSDN article out there that talks about testing or patterns or something like that, but there will probably be many times more articles about Silverlight, new features in .NET 4.0, and the like. All of that stuff is good, but I feel people are skipping over the essentials. You can probably pass any number of Microsoft certification tests without knowing much about test driven development or design patterns.

This is why I identify with the ALT.NET way of thinking. ALT.NET generally believes in these principles:

1. You’re the type of developer who uses what works while keeping an eye out for a better way.
2. You reach outside the mainstream to adopt the best of any community: Open Source, Agile, Java, Ruby, etc.
3. You’re not content with the status quo. Things can always be better expressed, more elegant and simple, more mutable, higher quality, etc.
4. You know tools are great, but they only take you so far. It’s the principles and knowledge that really matter. The best tools are those that embed the knowledge and encourage the principles (e.g. Resharper.)

Promoting ALT.NET or starting some new kind of cool kids club is not the point. The point is that good software design practices and principles are very important (many would say of the utmost importance), much more important than the latest shiny new tool that is coming out. If the code you write is not well designed or well tested, that problem is not going away by moving to .NET 4.0 or Silverlight.

So if you want to learn good software patterns and practices, here’s where I would start:

  • Learn how to do test-driven development. The best way is to have someone teach you how to do it because it’s hard to just read about it and pick it up (although you’re more than welcome to try!). If you don’t have someone to teach you, hopefully the next technical conference or Day of .NET or user group you go to will have a talk on how to do TDD. Go to it. I have lots of TDD links and some practice projects here.
  • Learn the SOLID principles and why they’re important. You can go buy this book or just read everything on this page. Again, if you next conference/user group/etc. has a talk on SOLID (and a lot of them will), go to it.
  • Read some of the “classics”, like some of the books mentioned here.
  • Make sure you keep your ego in check. We should never stop learning, and should never be content with where we are now. There will always be something worth knowing that you don’t know.