Wednesday, 24 February 2010

Improving TDD skills via treatment of test infection

TDD is a technique that can be used as an effective design tool. However it is quite easy (or was for me) to become "test infected". Normally this term is used as a positive description of someone who has seen how good TDD is and doesn't like to work without it, but in some cases this infection can turn nasty.

Symptoms of serious infection include becoming incapable of working without tests, an insatiable hunger for reference material on TDD showing the way to do it right (a worrying delusion, as there is no silver bullet), and a severe dependency on the process to the point where the patient sometimes gets blocked waiting for the tests to give them an answer to a question that they really just need to nut out for themselves.

As I mentioned in my last post on driving different aspects of code using TDD, it is really the developer using the tests to drive code, rather than the process itself driving the developer to write great code. Unfortunately severe infections can result in the developer subconciously believing the latter. Luckily the prognosis for these cases is quite positive, the infection responding quite well to simple remedial action. In many cases the patient can recover to be even more effective than they were prior to infection.

If you believe you may be suffering from a severe case of test-infection, try this experiment: force youself to write some code without using tests. High level acceptance tests are ok, just not TDD or unit tests. Something like the Karate Chop kata is ideal, because it is something that is normally very nice to drive with classical TDD tests.

When I tried this and a few similar exercises I initially found them suprisingly difficult without TDD. These were basic problems I would have solved beautifully prior to learning TDD, and here I was struggling on the same, simple exercises, all because I couldn't lean on my favourite process. I had developed quite a severe case. I've also seen this experiment trip up other, far more intelligent people in the same way.

I had become a slave to the process to the point where I had surrended some of the critical thinking I used to use before picking up TDD. I actually had to retrain myself to think first by repeating a few of these simple kata-like exercises until I was able to code again without the aid of TDD. I could then use my newly-rediscovered ability to think to target how I applied TDD (or other techniques) to problems. I'm confident this process can work for other patients as well. This is not to say patients need to enjoy working without the guidance of tests, but they should be able to code and design by thinking through the problem instead of relying on TDD. TDD is a tool, not the goal.

Once a developer gets to the point where they can write decent code both with and without TDD, they are now in a great position to wield TDD really effectively as a design tool. Rather than being driven by tests, or by designing upfront or on-the-fly without the feedback given by TDD, the developer can start writing tests that push both the design and implementation in the direction he or she feels it should go. The tests aren't driving: they are a means to an end, a stepping stone the developer uses to push the design to a point where the current feature can be implemented safely, quickly and cleanly. I think this level is where true mastery of TDD is achieved.

One day I hope to get there, but for now I'm happy to keep honing my fairly blunt use of TDD in the knowledge I'm using it to help me flounder along in the basic direction I want to go, rather than as a crutch.

Recovered from your own bout of test infection to become a better TDDer? I'd love to hear about it -- please leave a comment! :)

Tuesday, 23 February 2010

What exactly is TDD driving?

Over the last few months I've been toying around with some fairly wacky ideas about Test Driven Development. Ever since first discovering TDD a few years back (early 2006 I think) I've been hooked. It is such an awesome idea, with the seductive promise of helping to write cleaner, more elegant code with less bugs, less bloat, and more malleable designs.

The problem for me has been that it is tremendously difficult to get any sort of proficiency with the technique, especially when taking it out of the realms of practice exercises and into the workplace. Now, admittedly, a key contributor to these difficulties are my own limitations, including only fairly rudimentary OO design skills and difficulty refactoring, but at the same time I'd always hoped that TDD would provide the design feedback and guidance I needed to help counteract my shortcomings.

And so, in a valiant effort to avoid blaming my own lack of competence, I've started to challenge one of the fundamental tenants of classical* TDD: what if TDD does not really drive design?

* By classical TDD, I am referring to the case-by-case build up of the unit under test, potentially building the system bottom up, and in constrast to mockist TDD, BDD, and to a lesser extent scenario-based TDD. This was how I got my introduction to TDD, and is the style I normally see for introductory training, articles and posts.

Warning: this post is taking a fairly whacky idea and running with it far beyond what is reasonable. The purpose of this is to challenge myself to argue against a long-held belief of mine. It is a rant, so please take it with a grain of salt. :)

Driving what?

Most examples of classical TDD I've seen use what I refer to as testing data. That is, each test case provides one data point. The code written to pass the first test deals exclusively with that data point. Subsequent tests are passed by dealing with the next data point, in addition to the previous data points. After each test the code is refactored to reduce duplication.

An example of this could be test-driving a stack implementation using cases like "new stack should have zero items", "pushing one item should give count of one", "pushing two items should give count of two", "pushing one then popping one should give count of zero" etc. The reason I refer to this as "testing data" is because the tests are feeding the code different instances of data, rather than dealing with the general logic that differentiates the data. For example, we could check the logic that whenever an item is pushed onto the stack, the count is incremented (not the best example, but it shows the difference in approaches).

This approach of testing data seems to be the common way of practicing traditional TDD, at least from the material I've read (like the description in the classic Bowling Game example by Robert C. Martin (a.k.a. Uncle Bob) and Robert S. Koss -- it also happens to be a really great article).

So what is this approach to TDD driving? Well the first obvious thing to me (YMMV) is it is driving a fairly minimalist implementation. By only writing enough code to pass each test case and refactoring away duplication it helps us write just enough implementation to get it to work. The second thing I notice is the refactoring step is going to help keep the code clean, removing duplication, extracting well-named pieces of of functionality etc. Finally, by writing the tests from the perspective of a caller into your class under test, you are driving a usable API design.

There seems to be lots of driving being done, but how much are our tests really helping to drive the design? Well, in my view they seem to drive the implementation more than the design. By building up an implementation one data-point at a time my tests are giving me feedback as to what my code should do, not what the design should be. That is being left purely up to my design skills and nose for code smells.

The refactoring step can help me get a better design, but again, how much are the tests helping me with this? The tests can give me pain when trying to test all the data permutations, but they're not giving me much information on how to fix it. I've also found it is easy for this style of TDD to result in tests that provide resistance to refactoring attempts when we do try to fix it, as they are so tied to the results of the implementation that it can become difficult to push out logic to other classes without potentially coupling an entire object tree. (Yes, I realise I'm probably doing it wrong, but my point is the feedback from the tests haven't helped me to overcome this incompetence.) I most commonly notice this when I extract a class and then wonder how much of the tests I should push down into a dedicated fixture for that class, and whether I should change the initial fixture(s) to keep the tests focused and unit-sized.

Now the driving of the API design I'll pay -- it is hard to argue that any form of TDD does not pay off here. However most of the feedback here is with the API of the class under test. The API of any collaborators introduced while refactoring is purely up to our design knowledge.

I'm not completely sold on the idea, but I can't quite shake the feeling that my tests aren't really helping drive my design as much as I'd initially thought. By concentrating on implementing the code for each case of data, pehaps the main thing I am building up and getting feedback on is my implementation?

So what's wrong with driving implementation?

Well, nothing really. If it works for you then great! In some cases it's remarkably valuable. However it is not without downsides (what isn't?). As I mentioned above, I feel driving implementation with classical TDD tests can result in fragile tests that make refactoring harder. But my complaint goes a bit deeper than that.

By building up implementation case by case, I find it very easy to fall into a procedural-style implementation. For small exercises this is actually a feature -- the process eliminates unnecessary cruft. But for larger systems, where abstraction becomes more useful (or even essential), the emphasis shifts to collaborations and the behaviour of components. For these cases the tests don't give much feedback on what my design should be, what my abstraction should be, or who the collaborators should be. They only really give me feedback when it all goes wrong, by which time I may be lumped with a procedural design and a suite of fragile, data-dependent tests.

Another issue I've hit before is test-driving the implementation of algorithms, the most infamous example of which is probably sudoku solving. Now I definitely don't agree with criticism of Ron about his series -- he intentionally went into the problem "blind" to see what would happen. His experience leads to some interesting ideas. One thing I take from it is that while driving implementation can provide nice implementations of very simple algorithms, you really have to understand the fundamental algorithm before test driving the implementation. As Peter Norvig (the other sudoku solver-solver) beautifully put it:

"I think test-driven design is great... But you can test all you want and if you don’t know how to approach the problem, you’re not going to get a solution." -- Peter Norvig, as quoted in 'Unit testing in Coders at Work' by Peter Seibel

Why is this relevant? Well, if you need to understand the algorithm, or more generally, the problem you are tackling, to produce the implementation, then the tests are potentially not driving that much at all. Instead they're helping you check the correctness of each step of the algorithm you are solving -- a handy safety net for catching programming errors, and a useful technique for focusing on small pieces at a time, but not a tool for driving the design or the abstractions we're using to break down the problem.

When broken down to this level, building up your implementation in terms of the steps of an algorithm you understand, it raises another question: how hard is implementation really? If we know all the steps then we just need to translate that into code. Admittedly us programmers stuff this up much of the time, but we could also catch these problems with standard unit tests. And TDD is meant to be about much more than unit testing. To me, while difficult, implementation is not that hard -- the real trouble is in getting a good design, particularly when it comes to OO and abstractions.

TDD Adoption: As an aside, I attribute most of the complaints I've read about TDD to this blurring of the ideas of driving implementation vs. driving design. An interesting article, Problems with TDD, outlines the problems Andrew Dalke experienced when looking at TDD. Most of the examples show data-driven tests used for the implementation of algorithms. I think the people that find TDD particularly grating may have a better experience focusing on driving the design via their tests.

How to drive design?

So what options do we have for test driving design? Well, for our tests to give us feedback on our design we'll need them to focus on that aspect rather than the implementation and data. That means isolating the class under test from dependencies and defining the results of collaborating with them, be it in terms of state or behaviour.

One way I've found to do this is using the steps I picked up from JP's Nothin' but .NET course. This approach focuses on writing tests that assert the result of exercising the class under test's single responsibility in a particular context, where the context is just the configuration of class' collaborators. The context becomes the test fixture setup.

Here much of the design of the collaborators (and the abstractions required to solve the problem at hand) is driven through the context. The implementation of the class under test is tested with regard for this context, and eliminates generating a whole raft of data-based tests in favour or specifying and testing the step in the algorithm this class plays.

Here the context/setup is providing valuable feedback on your design. Is it hard to setup? Are there too many dependencies? Are there too many / too complicated interactions? Is this abstraction right for this step of the problem? The test cases also provide design feedback -- is your class under test doing too much? Violating the Single Responsibility Principle (SRP)? And we also get verification of the correctness of our implementation via our assertions.

Having the collaborations defined in the context/setup also helps ease refactoring. We can introduce new or change existing abstractions and collaborators by tweaking the context, leaving the actual test, responsibility and assertion unchanced (alleviating some of the problems faced extracting classes that I mentioned earlier).

I've tried to write up an example of this approach by tackling the StringCalculator kata. Of course, the problem with writing up bloggable examples like this is they end up hopelessly over-engineered for their size and complexity, but hopefully with some imagination you can see how scaling this to more complex problems provides a way of driving a neatly abstracted design (in some cases insanely abstracted ;)).

Another approach that is a bit less extreme and compromises between testing design and implementation is what I refer to as scenario-based testing. This puts more emphasis on context than classical TDD, but also tends to favour more test cases per scenario than my interpretation of "BooDhoo-D". :)

When driving implementation is appropriate

All this ranting has been fun and all, but I'm not going to pretend for even a nanosecond that there is a silver bullet lurking around here somewhere. You need to pick the right tool for the job. It's important to remember that it is not the tests themselves driving anything, it is the developer writing the tests necessary to drive their code to a solution. The developer will still have to rely on their own design and coding skills, and it is a matter for them to decide how their tests can best provide feedback to make the most of these skills.

I've found using my tests to drive implementation is more than appropriate for the bottom, most concrete parts of my apps -- at these layers we get all the classical TDD advantages we noticed with our small practice exercises and I find it a prerequisite for me to write good code. For the majority of classes, I'm currently finding it easiest to use my tests to drive my designs by taking a different approach to that described in classical TDD literature, focusing on collaborators and contexts.

I am also convinced that anyone with a good head for OO design and refactoring will find classical TDD much easier than I do. They'll be able to take any pain they experience with the tests, and safely refactor both test and production code to introduce the necessary abstractions to get a good design.

However you prefer to work, the more tools you have at your disposal, and the more your understand the mechanism by which they work, the more luck you're going to have with your code. Hopefully this has given you some ideas as to how to focus TDD to best suit the way you work.

Conclusion

This has been a wild rant. I definitely don't believe all the points above as strongly as I have written them, but I can't quite shake the idea that somewhere in this rambling is a grain of truth that might help someone out.

Bottom line is that TDD is awesome -- my pursuit of learning it has taught me so much and rekindled a once-waning love for programming, but it's tough. If you have problems with it don't be afraid to try completely whacky ideas and approaches in an effort to get different forms of feedback on different aspects of your code. If this post encourages you to do that, then my mission has been accomplished for today. :)

Monday, 15 February 2010

An attempt at simple MVVM with WPF

I have very little practical experience with MVVM and WPF, so I thought it would be incredibly useful for me to post some misinformation on the subject. Please feel free to correct any (or all) of my mistakes in the comments or via email, and I'll update the post accordingly.

At work a few of us went through a very simple practice exercise to try and get the basics of the pattern. The goal was to create a screen that accepts two numbers and displays their sum, but do it using MVVM as if it were a real problem rather than a trivial one.

MVVM, or Model - View - ViewModel (a.k.a. Presentation Model), is a separated presentation pattern for use with frameworks like WPF that have good support for data-binding and other view interations. This differs from Model View Presenter (MVP) in that MVP tends to be more prescriptive, the presenter explicitly controlling everything the view does (at least in the Passive View incarnation of the pattern). This tends to result in a lot of plumbing code (these items should go in this list on the view, when a new item is selected the view should display a new list of items, etc.).

MVVM relies on databinding, commands etc. in the framework to remove some of this plumbing code, leaving us to focus on the logical model of our view (or the ViewModel), and our application/domain logic (the Model). The View is then just the physical representation of our logical ViewModel, containing no logic other than how to physically represent the logical view.

For this example we'll design a view for our adder screen, test drive a ViewModel representation of the screen, then wire them together using WPF databinding.

Designing our view

For our very useful Adder screen we'll need two text boxes so the user can enter the numbers to add, and a label that will display the result of the addition. Whenever one of the numbers is changed our result will need to be updated to reflect the number total. When we are done it might look a bit like this:

Mockup of how the adder application should look.

Because we are separating the physical from logical representation of the view, we will fairly easily be able to change how this looks, but in terms of how our screen will logically function, this contains all the details we need.

Test driving the ViewModel

Our view design requires input for two numbers. Whenever those numbers change our result will change. We'll write a test for that, and make it pass:

public class AdderViewModelTests {
    [TestFixture]
    public class When_both_numbers_are_provided {
        private Adder _adder;
        private AdderViewModel _adderViewModel;
        private int _expectedResult;

        [SetUp]
        public void SetUp() {
            const int firstNumber = 10;
            const int secondNumber = 123;
            _expectedResult = 4545;
            
            _adder = MockRepository.GenerateStub<Adder>();
            _adder.Stub(x => x.Add(firstNumber, secondNumber)).Return(_expectedResult);
            _adderViewModel = new AdderViewModel(_adder);
            
            _adderViewModel.FirstNumber = firstNumber;
            _adderViewModel.SecondNumber = secondNumber;
        }

        [Test]
        public void Should_set_result_from_adder() {
            Assert.That(_adderViewModel.Result, Is.EqualTo(_expectedResult));
        }
    }
}

public class AdderViewModel {
    private readonly Adder _adder;
    public AdderViewModel(Adder adder) { _adder = adder; }
    public int FirstNumber { get; set; }
    public int SecondNumber { get; set; }
    public int Result { get { return _adder.Add(FirstNumber, SecondNumber); } }
}

This test is specifying that our AdderViewModel will use an Adder, and the result of the call to _adder.Add(firstNumber, secondNumber) will be used to set the Result in the view model.

Now, logically, the element displaying our result will need to know when a new result is calculated so it can update. One way to do this in WPF is to make our ViewModel implement INotifyPropertyChanged and use a property changed event whenever the result changes . Let's add a test for that (there are better ways of doing this, but this will do for now):

[SetUp]
public void SetUp() {
    const int firstNumber = 10;
    const int secondNumber = 123;
    _expectedResult = 4545;
    
    _adder = MockRepository.GenerateStub();
    _adder.Stub(x => x.Add(firstNumber, secondNumber)).Return(_expectedResult);
    _adderViewModel = new AdderViewModel(_adder);
    RecordPropertyChangeOnViewModel();

    _adderViewModel.FirstNumber = firstNumber;
    _adderViewModel.SecondNumber = secondNumber;
}

private void RecordPropertyChangeOnViewModel() {
    _adderViewModel.PropertyChanged += (sender, args) => _propertyChanged = args.PropertyName;
}

[Test]
public void Should_indicate_that_result_has_changed() {
    Assert.That(_propertyChanged, Is.EqualTo("Result"), "Should have raised property changed event for Result property");
}

[Test]
public void Should_set_result_from_adder() {
/* ... snip ... */

One thing I want to make absolutely, 100% clear here: our tests are not driving this design decision. We are doing this whole INotifyPropertyChanged thing because we know it is one of the ways to get our WPF view to update the displayed result. This is something that has tended to annoy me greatly when reading TDD UI examples, these patterns seem to pop out from nowhere and I can never see the link between the tests and the design. In this case there isn't any. I'll save my rant for another day, but just be aware that while tests can drive design, you still need to be aware of the basic direction you are heading: the fundamental algorithm you are building, and the UI framework you'll be interacting with. When I did this exercise I didn't even have the changed event tested. But it is an easy enough test to write, and is essential for our binding to work, so we may as well include it (before the code, after the code, it's all good!).

We start by making AdderViewModel implement INotifyPropertyChanged, then add the PropertyChanged event. We'll then want to fire this event whenever the Result changes. As the result changes as the numbers do, we can do this from the setters of our FirstNumber and SecondNumber properties. While we could just raise the property changed event directly from these setters, I think it makes more sense to raise this when the Result itself changes. To me it is a bit more intention-revealing that way (in other situations where we had delayed execution or an out-of-proc service call then it would also make more sense to update when the value is set rather than when a dependency changes).

Update 2010-05-02: There is a problem with this approach! The ViewModel is meant to be a logical representation of the physical view. In the implementation below, if we set the FirstNumber or SecondNumber properties via code then there will not be PropertyChanged events raised specifically for those properties. As a result, the data-binding on our view will not be notified of the update and the text boxes will still show the old numbers. Not a good representation of our view!

In this case we're only setting these properties from the view, so we won't see the problem, but the fact our ViewModel does not adequately represent the view could cause problems later. To fix it we can raise PropertyChanged when any data-bindable properties are changed (not just when Result changes). We can cover this with tests just as we did for when Result changes.

Thanks a lot to Hüseyin Tüfekçilerli for pointing this out in the comments.

public class AdderViewModel : INotifyPropertyChanged {
    private readonly Adder _adder;
    private int _firstNumber;
    private int _secondNumber;
    private int _result;

    public AdderViewModel(Adder adder) {
        _adder = adder;
    }

    public int FirstNumber {
        get { return _firstNumber; }
        set { _firstNumber = value; NumberChanged(); }
    }

    public int SecondNumber {
        get { return _secondNumber; }
        set { _secondNumber = value; NumberChanged(); }
    }

    private void NumberChanged() {
        Result = _adder.Add(_firstNumber, _secondNumber);
    }

    public int Result {
        get { return _result; }
        set { _result = value; OnResultChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnResultChanged() {
        var handler = PropertyChanged;
        if (handler == null) return;
        handler(this, new PropertyChangedEventArgs("Result"));
    }
}

Test's pass, so let's hit the XAML.

Implementing a physical view over our view model

When I created my project for this exercise I used the basic WPF Application template, which creates an App.xaml and MainWindow.xaml. Let's just get an ugly MainWindow working first.

<Window x:Class="WpfCalculatorKata.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Adder" Width="200" Height="200">
    <StackPanel>
        <TextBox Name="firstNumber" Text="{Binding FirstNumber}" />
        <TextBox Name="secondNumber" Text="{Binding SecondNumber}" />
        <Label Name="result" Content="{Binding Result}" />
    </StackPanel>
</Window>

Here we're just shoving two textboxes and one label into a stack panel, and attempting to bind each control to the relevant property on the view model.

A word or two on XAML

We now interrupt this example to provide a 5 second overview of XAML. The easiest way I've found to think of XAML is an XML version of C#'s object initialisation syntax. For example, when I see <TextBox Name="FirstNumber" Width="120" /> I think of something more like this:

TextBox textBox = new TextBox {Name = "FirstNumber", Width = 120};

Similarly for the StackPanel node shown above, I picture using a list initialisation to populate the control's Children property (or Content property for most other controls):

StackPanel stackPanel = new StackPanel { 
    Children = new List<Control> {
                    new TextBox {Name = "FirstNunber"},
                    new TextBox {Name = "SecondNumber"}
                }};            

This won't actually compile as the Children property is not settable, but the basic idea is the same. Nested elements get added to a property of the parent object (for many controls this is the Content property, but the exact mapping is object-specific).

In the process of building the project, the compiler will create the code required to instantiate each object, initialise its properties (mainly via the XML attributes on the node), and basically build up a nice, big object graph. It also has some additional smarts to handle binding and other expressions, cascading properties etc. I'm not sure if this helps you at all, but this idea really helped demystify XAML for me, even if it is not an exact representation of what actually occurs.

Getting our view to work

How does our app know what {Binding FirstNumber} means? Well, if you try and run the code we have at the moment it's not going to work. While binding WPF searches the control's DataContext for the specific (and public!) property. If it can't find that it will check the parent's DataContext, and the parent's parent's and so on up the object tree. What we want to do is set our view's DataContext to our view model. We can easily do this in the MainWindow.xaml.cs code behind:

public partial class MainWindow : Window {
    public MainWindow(AdderViewModel viewModel) {
        DataContext = viewModel;
        InitializeComponent();
    }
}

Unfortunately our App.xaml is expecting MainWindow to have a default constructor, so we'll need to remove the startup attribute and override the application started event to show our view. For a real application this would be a great place for an IoC container call and/or screen activator, but we'll just new it up for now.

<Application x:Class="WpfCalculatorKata.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>         
    </Application.Resources>
</Application>
public partial class App : Application {
    protected override void OnStartup(StartupEventArgs e) {
        var window = new MainWindow(new AdderViewModel(new ConreteAdder()));
        window.Show();
    }
}

public class ConreteAdder : Adder {
    public int Add(int firstNumber, int secondNumber) {
        return firstNumber + secondNumber;
    }
}

You'll notice that to create our AdderViewModel we need an implementation of our Adder interface. I've just chucked in a ConcreteAdder here, completely and utterly uncovered by automated testing (gasp!). It looks close to right from here though, so let's run it.

Screenshot of the basic adder application

Here we see our UI, happily adding the numbers it's given. As an aside, if you enter an invalid number while debugging you'll see data binding errors appear in the output window, but the application will continue running. If you've got bindings setup but nothing is happening when you run the app, check the output window and see if there are any messages there.

Updating our view to match our initial mockup

To get a similar UI to our initial mockup, we just need to add some labels to our textboxes and adjust the spacing on our form. We'll use a Grid instead of our original StackPanel so we can easily align our labels with their textboxes, and we'll use a style to hard code in the height and vertical alignment of our text boxes. The final XAML and application look like this:

<Window x:Class="WpfCalculatorKata.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Adder" Width="250" Height="200">
    <Window.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Height" Value="23" />
            <Setter Property="VerticalAlignment" Value="Top" />
        </Style>
    </Window.Resources>
    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Label Content="First Number" Grid.Row="0" />
        <Label Content="Second Number" Grid.Row="1" />
        <Label Content="Result" Grid.Row="2" />
        <TextBox Name="firstNumber" Text="{Binding FirstNumber}" Grid.Row="0" Grid.Column="1" />
        <TextBox Name="secondNumber" Text="{Binding SecondNumber}" Grid.Row="1" Grid.Column="1" />
        <Label Name="result" Content="{Binding Result}" Grid.Row="2" Grid.Column="1"/>
    </Grid>
</Window>
Screenshot of the adder application with similar layout to the initial mockup.

Conclusion

We've implemented a tremendously useful calculator (well, more correctly, a completely useless small integer adder) using MVVM and WPF. We represented the logical view of our screen using a testable AdderViewModel, which we then bind to from our XAML view. While we used TDD to flesh out how the AdderViewModel was going to interact with our domain model (the Adder), we also needed to know enough about the end goal (to bind to a WPF form) to tell us which tests to write -- our tests were not magically going to guide us to a bindable view model. Finally we changed our physical view to better match the layout of our initial mock up, but didn't have to change any non-XAML code as we did not change the logical model of the view.

Feel free to download the code and have a play. Hope this helps! And remember to correct any n00b mistakes I've made. :)

Saturday, 6 February 2010

Refactor or redesign?

I've only very recently started to differentiate refactoring from redesigning, and I've found it a very useful (albeit somewhat artificial) delineation to keep in mind whenever changing code.

Refactoring is the process of making small, behaviour-preserving changes to the design of your code. While refactoring can be considered a form of redesign, I'm beginning to separate both concepts along the lines of scope. When I talk about redesign I'm talking about a major change to the design, where the change has a large scope and the resulting design will bear little to no resembalance to the previous one.

So why do I feel it is beneficial to separate these two ideas? Because I'm starting to think that redesign is rarely the right thing to do.

Refactoring should probably rarely (if ever) take longer than an hour (hopefully much less -- it should probably fit into a pomodoro), whereas redesign can take a full day, maybe even a week or more. Refactoring will probably touch a couple of classes. Redesign may touch a lot more, including across different levels of abstraction and along dependency chains. Refactoring will keep all tests passing and the code compiling (besides brief moments spent leaning on the compiler). Redesigns may go several minutes at a time with the local build breaking. Refactoring is generally motivated by removing a code smell such as duplicaton from code. Redesign is generally motivated by a feeling of "this is a complete mess, I'm never going to be able to work with this until it is tidied up", or sometimes "this will make it much easier to add features in future".

One of the points I wrote about in my last post was the importance of momentum to projects. A redesign is a big drain of momentum, as you are not actually adding features during that time. What's worse is that you'll tend to be redesigning for your perceived future needs, rather than as a direct response to your immediate requirements (e.g. "this is messy, so I'll need to clean it up otherwise it will cause trouble later"). Yes, you need to clean up your technical debt, but if you can do it with small refactorings toward a better design, then you can use the feedback you get during later stories to keep the design flexible. There is a real risk of a redesign turning into a big design upfront, with all the disadvantages that entails.

On my last project I experienced the "joy" (admittedly all my own fault) of working for over a week on a redesign to allow a feature to be added more easily into the code, only to have a feature come up in the very next sprint that required us to undo a significant amount of the redesign work. Today I started to make a similar mistake, encouraging my pair to make a fairly big change to our presenters to make our feature easier to add. This required removing a whole lot of duplication, factoring out a common base class or extracting a whole lot of behaviour into strategies before we could even begin on the real work. It was only meant to take a day or so, but towards the end of the day it still wasn't coming together nicely.

After a chat with our bearded architect, we decided to try just refactoring the part of the code that would be affected by the change. Rather than removing all the duplication, we just pulled out a base class for a small subset of the presenters and pushed and pulled a view members up and down to correct the inheritance relationship that was causing us problems. The effort took about an hour, including updating a whole lot of test code from the previous mess.

Although still not really clean, our design is now a step closer to neat, and will allow us to add the feature with relative ease (the refactoring was guided entirely by that feature's needs, not by guesses as to what other features would also require). The next time we come across trouble with that section of code, we can take another refactoring step to remove even more duplication, but unlike my first misguided attempt we can be guided by the new requirement, so we'll have more information on which direction to coax the design.

So from now on I'm going to be really careful to try and stick to refactoring, and resist the siren song of redesign. If there are parts of the code screaming out for redesign then I'll start making small refactorings in the right direction, but never make large changes to jump to a new design if I can possibly avoid it. One thing I need to remember is that the design is never going to be perfect, so there is little point investing too much time on trying to get it there. If instead I concentrate on heading towards a better design by being careful to always leave the code cleaner than I found it, then I should be able to strike a good balance between cleaning the design and adding features, which should help both the overall design as well as my velocity.