Wednesday, June 27, 2007

ReSharper plugin for cyclomatic complexity

I was giving a demo the other day and that showed ReSharper's cyclomatic complexity plugin. It puts a squiggly underneath method declarations that have a cyclomatic complexity over 15, and also tells you what the cyclomatic complexity is:

It's a nice little plugin that lets me have some visual cues on methods that need refactoring. Of course, looking at the method body itself provides a good clue also. You can find this plugin, along with a few others, here:

http://www.jetbrains.net/confluence/display/ReSharper/PowerToys+setup+files

Yet another reason to love ReSharper.

Monday, June 25, 2007

Generic Value Object Equality

I read a post from Oren the other day where he posted some code for a generic Entity base type that implemented the correct equality logic.  I realized that I've needed a generic base type for Value Objects as well.

Value Object Requirements

In the Domain Driven Design space, a Value Object:

  • Has no concept of an identity
    • Two different instances of a Value Object with the same values are considered equal
  • Describes a characteristic of another thing
  • Is immutable

Unfortunately, in nearly all cases I've run in to, we can't use Value Types in .NET to represent Value Objects.  Value Types (struct) have some size limitations (~16 bytes or less), which we run into pretty quickly.  Instead, we can create a Reference Type (class) with Value Type semantics, similar to the .NET String type.  The String type is a Reference Type, but exhibits Value Type semantics, since it is immutable.  For a Reference Type to exhibit Value Type semantics, it must:

  • Be immutable
  • Override the Equals method, to implement equality instead of identity, which is the default

Additionally, Framework Design Guidelines has some additional requirements I must meet:

  • Provide a reflexive, transitive, and symmetric implementation of Equals
  • Override GetHashCode
  • Implement IEquatable<T>
  • Override the equality operators

Generic Implementation

What I wanted was a base class that would give me all of the Framework Design Guidelines requirements as well as the Domain Driven Design requirements, without any additional logic from concrete types.  Here's what I ended up with:

public abstract class ValueObject<T> : IEquatable<T>
    where T : ValueObject<T>
{
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;

        T other = obj as T;

        return Equals(other);
    }

    public override int GetHashCode()
    {
        IEnumerable<FieldInfo> fields = GetFields();

        int startValue = 17;
        int multiplier = 59;

        int hashCode = startValue;

        foreach (FieldInfo field in fields)
        {
            object value = field.GetValue(this);

            if (value != null)
                hashCode = hashCode * multiplier + value.GetHashCode();
        }

        return hashCode;
    }

    public virtual bool Equals(T other)
    {
        if (other == null)
            return false;

        Type t = GetType();
        Type otherType = other.GetType();

        if (t != otherType)
            return false;

        FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        foreach (FieldInfo field in fields)
        {
            object value1 = field.GetValue(other);
            object value2 = field.GetValue(this);

            if (value1 == null)
            {
                if (value2 != null)
                    return false;
            }
            else if (! value1.Equals(value2))
                return false;
        }

        return true;
    }

    private IEnumerable<FieldInfo> GetFields()
    {
        Type t = GetType();

        List<FieldInfo> fields = new List<FieldInfo>();

        while (t != typeof(object))
        {
            fields.AddRange(t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));

            t = t.BaseType;
        }

        return fields;
    }

    public static bool operator ==(ValueObject<T> x, ValueObject<T> y)
    {
        return x.Equals(y);
    }

    public static bool operator !=(ValueObject<T> x, ValueObject<T> y)
    {
        return ! (x == y);
    }
}

I borrowed a little bit from the .NET ValueType base class for the implementation of Equals.  The ValueObject<T> type uses reflection to access and compare all internal fields for Equals, as well as for GetHashCode.  All implementers will need to do is to ensure that their concrete type is immutable, and they're done.

I could probably optimize the reflection calls and cache them, but this implementation is mainly for reference anyway.

The Tests

Just for completeness, I'll include the set of NUnit tests I used to write this class up.  I think the tests describe the intended behavior well enough.

[TestFixture]
public class ValueObjectTests
{
    private class Address : ValueObject<Address>
    {
        private readonly string _address1;
        private readonly string _city;
        private readonly string _state;

        public Address(string address1, string city, string state)
        {
            _address1 = address1;
            _city = city;
            _state = state;
        }

        public string Address1
        {
            get { return _address1; }
        }

        public string City
        {
            get { return _city; }
        }

        public string State
        {
            get { return _state; }
        }
    }

    private class ExpandedAddress : Address
    {
        private readonly string _address2;

        public ExpandedAddress(string address1, string address2, string city, string state)
            : base(address1, city, state)
        {
            _address2 = address2;
        }

        public string Address2
        {
            get { return _address2; }
        }

    }

    [Test]
    public void AddressEqualsWorksWithIdenticalAddresses()
    {
        Address address = new Address("Address1", "Austin", "TX");
        Address address2 = new Address("Address1", "Austin", "TX");

        Assert.IsTrue(address.Equals(address2));
    }

    [Test]
    public void AddressEqualsWorksWithNonIdenticalAddresses()
    {
        Address address = new Address("Address1", "Austin", "TX");
        Address address2 = new Address("Address2", "Austin", "TX");

        Assert.IsFalse(address.Equals(address2));
    }

    [Test]
    public void AddressEqualsWorksWithNulls()
    {
        Address address = new Address(null, "Austin", "TX");
        Address address2 = new Address("Address2", "Austin", "TX");

        Assert.IsFalse(address.Equals(address2));
    }

    [Test]
    public void AddressEqualsWorksWithNullsOnOtherObject()
    {
        Address address = new Address("Address2", "Austin", "TX");
        Address address2 = new Address("Address2", null, "TX");

        Assert.IsFalse(address.Equals(address2));
    }
    
    [Test]
    public void AddressEqualsIsReflexive()
    {
        Address address = new Address("Address1", "Austin", "TX");

        Assert.IsTrue(address.Equals(address));
    }

    [Test]
    public void AddressEqualsIsSymmetric()
    {
        Address address = new Address("Address1", "Austin", "TX");
        Address address2 = new Address("Address2", "Austin", "TX");

        Assert.IsFalse(address.Equals(address2));
        Assert.IsFalse(address2.Equals(address));
    }

    [Test]
    public void AddressEqualsIsTransitive()
    {
        Address address = new Address("Address1", "Austin", "TX");
        Address address2 = new Address("Address1", "Austin", "TX");
        Address address3 = new Address("Address1", "Austin", "TX");

        Assert.IsTrue(address.Equals(address2));
        Assert.IsTrue(address2.Equals(address3));
        Assert.IsTrue(address.Equals(address3));
    }

    [Test]
    public void AddressOperatorsWork()
    {
        Address address = new Address("Address1", "Austin", "TX");
        Address address2 = new Address("Address1", "Austin", "TX");
        Address address3 = new Address("Address2", "Austin", "TX");

        Assert.IsTrue(address == address2);
        Assert.IsTrue(address2 != address3);
    }

    [Test]
    public void DerivedTypesBehaveCorrectly()
    {
        Address address = new Address("Address1", "Austin", "TX");
        ExpandedAddress address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX");

        Assert.IsFalse(address.Equals(address2));
        Assert.IsFalse(address == address2);
    }

    [Test]
    public void EqualValueObjectsHaveSameHashCode()
    {
        Address address = new Address("Address1", "Austin", "TX");
        Address address2 = new Address("Address1", "Austin", "TX");

        Assert.AreEqual(address.GetHashCode(), address2.GetHashCode());
    }

    [Test]
    public void TransposedValuesGiveDifferentHashCodes()
    {
        Address address = new Address(null, "Austin", "TX");
        Address address2 = new Address("TX", "Austin", null);

        Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode());
    }

    [Test]
    public void UnequalValueObjectsHaveDifferentHashCodes()
    {
        Address address = new Address("Address1", "Austin", "TX");
        Address address2 = new Address("Address2", "Austin", "TX");

        Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode());
    }

    [Test]
    public void TransposedValuesOfFieldNamesGivesDifferentHashCodes()
    {
        Address address = new Address("_city", null, null);
        Address address2 = new Address(null, "_address1", null);

        Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode());
    }

    [Test]
    public void DerivedTypesHashCodesBehaveCorrectly()
    {
        ExpandedAddress address = new ExpandedAddress("Address99999", "Apt 123", "New Orleans", "LA");
        ExpandedAddress address2 = new ExpandedAddress("Address1", "Apt 123", "Austin", "TX");

        Assert.AreNotEqual(address.GetHashCode(), address2.GetHashCode());
    }

}

UPDATE: 6/27/07

  • Changed ValueObject<T> to implement IEquatable<T> instead of IEquatable<ValueObject<T>>
    • Equals reflects derived type instead of base type, since C# generics are not covariant (or contravariant), IEquatable<ValueObject<T>> != IEquatable<T>
  • Changed GetHashCode algorithm to use a calculated hash to cover additional test cases
    • Gather parent type field values
    • Fix transposed value bug
  • Fixed NullReferenceException bug when "other" is null for IEquatable<T>.Equals
  • Added tests to cover bugs and fixes

Saturday, June 23, 2007

Disable annoying computer beep

My work laptop would emit loud beeps occasionally when I hit the wrong button, performed an invalid action, or maybe just looked at it funny.  No volume controls could disable the beep, as it came from an internal speaker.  The beep is very loud and quite disturbing, and prompts many dirty looks from co-workers.  After some web sleuthing, I found a couple of commands that would stop and permanently disable the computer beep:

net stop beep

sc config beep start= disabled

Enter these two commands exactly into a command prompt.  Don't forget the space after the "=", it won't work without it.

The first command will stop the BEEP service, which is the Windows service responsible for constantly annoying me.  The second command disables the service completely, and will prevent it from starting up the next time you turn on your computer.  Now you can live happily beep-free, and your co-workers will thank you for it.

Thursday, June 21, 2007

Multi-Targeting support for VS 2008

So the official name for Visual Studio "Orcas" is Visual Studio 2008 - which is not as nice as "Silverlight", but it does give Microsoft a year of leeway to delay release.  One of the biggest pain points we've had is migrating to Visual Studio 2005 from Visual Studio 2003.  We wanted the nice new IDE, but that meant we had to upgrade all of our projects to .NET 2.0 to take advantage of the new IDE features.  This was complicated even more since the project files completely changed to MSBuild files, so migration was a such a huge pain, we didn't ever want to do it again.

In Visual Studio 2008, Microsoft introduced "Multi-Targeting", which allows the developer to select which version of the .NET Framework to target (2.0, 3.0, or 3.5).  Check out Scott Guthrie's blog entry on Multi Targeting for more details:

http://weblogs.asp.net/scottgu/archive/2007/06/20/vs-2008-multi-targeting-support.aspx

So we'll be able to get all the cool VS 2008 features such as JavaScript Intellisense for our VS 2005 projects, without needing to migrate the projects themselves.  Since the project file format won't change, migration to .NET 3.5 will be much smoother than from .NET 1.1 to .NET 2.0.

Some might see this as just a nice thing MS did to help developers out.  Since migration to VS 2005 left such a bad taste in so many developers mouths, I think this feature was absolutely necessary to provide to remove that barrier of entry for VS 2008.

Tuesday, June 19, 2007

The problem with code comments

Let me first state that I'm not proposing eliminating the use of code comments.  Code comments can be very helpful pointing a developer in the right direction when trying to change a complex or non-intuitive block of code.

Additionally, I'm also not referring to API documentation (like C# XML code comments) when I'm referring to code comments.  I'm talking about the little snippets of comments that some helpful coder gave to you as a gift to explain why this code is the way it is.  Code comments are easy to introduce, can be very helpful, so what's the big deal?

Code comments lie

Code comments cannot be tested to determine their accuracy.  I can't ask a code comment, "Are you still correct?  Are you lying to me?"  The comment may be correct, or it may not be, I don't really know unless I visually (and tediously) inspect the code for accuracy.

I can trust the comment and just assume that whatever it tells me is still correct.  But everyone knows the colloquialism about assuming, so chances are I'll be wrong to assume.  Who's to blame then, me or the author of the original comment?  It can be dangerous to assume that a piece of text that is neither executable nor testable is inherently correct.

Code comments are another form of duplication

This duplication is difficult to see unless I need to change the code the comment pertains to.  Now I have to make the change in two places, but one of the places is in comments.  If every complexity in code required comments, how much time would I need to spend keeping the original comments up to date?  I would assert that it takes as much time to update a code comment as it does to make a change on the code being commented.

Since the cost to maintain comments is high, they're simply not maintained.  They then fall into another pernicious category of duplication where the duplicate is stale and invalid.  When code comments are invalid, they actually hurt the next developer looking at the code because the comment may lie to the developer and cause them to introduce bugs, make the wrong changes, form invalid assumptions, etc.

Code comments are an opportunity cost

I think the real reason code comments aren't maintained is that most developers instinctively view them as an opportunity cost.  That is, spending time (and therefore money) to maintain code comments costs me in terms of not taking the opportunity to improve the code such that I wouldn't need the comments in the first place.  The benefits of making the code more soluble, testable, and consequently more maintainable are much more valuable than having up-to-date comments.

A worse side-effect is when developers use the time to update the comments to do nothing instead.  Call it apathy, ignorance, or just plain laziness, but more often than not the developer would rather leave the incorrect comments as-is and not worry about eliminating the need for comments.

Code comments are not testable

If I can't test a code comment, I can't verify it.  Untested or not testable code is by definition legacy code and not maintainable.  But code can be refactored and modified to be made testable and verifiable.  Code comments can't, so they will always remain not maintainable.  Putting processes in place to enforce code comments are up-to-date is not the answer since the fundamental problem with comments are I can't test to know if they are correct in any kind of automated or repeatable fashion.

Alternatives

So if code comments are bad (there are exceptions of course), what should I do instead?

  • Refactor code so that it is soluble
  • Refactor code so that it is testable
  • Use intention-revealing names for classes and members
  • Use intention-revealing names for tests

There are always exceptions to the rule, and some scenarios where code comments are appropriate could be:

  • Explaining third-party libraries, like MSMQ, etc. (that should be hidden behind interfaces anyway)
  • Explaining test results (rare)
  • Explaining usage of a third-party framework like ASP.NET where your code is intimate with their framework

I'd say 99 times out of 100, when I encounter a code comment, I just use Extract Method on the block being commented with a name that might include some of the comment.  Tools like ReSharper will actually examine your code comments and suggest a good name.  When the code comment block is extracted in a method, it's testable, and now I can enforce the behavior through a test, eliminating the need for the comment. 

Friday, June 15, 2007

Choosing between constants or static fields

I recently needed to add some code to our project that pulled some information out of configuration.  The configuration is broken into sections and values, and I need to give the API a section name and a key name to retrieve the configuration value:

string maxCouponLimit = configManager.GetValue("storeOptions", "maxCouponLimit");

The "storeOptions" configuration section in the config file had several other config values, so when I searched the solution for "storeOptions", I found several dozen references of the exact same code getting different values out of the same section.  This was a prime candidate for the Replace Magic Number with Symbolic Constant refactoring.  When I look around the Framework Class Library, I couldn't find any constants, however.  Why is this?

Refactoring to a constants class

Let's say I want to put the "storeOptions" value in a constants class:

public static class ConfigurationSections
{
public const string STORE_OPTIONS = "storeOptions";
}

I have defined both a constant and a readonly static field for the configuration value.  To illustrate how constants work first, I'll change all references to "storeOptions" to use "ConfigurationSections.STORE_OPTIONS" instead.  As it turns out, Joe from the accounting team also uses this configuration section in his assemblies, so I work with Joe to change all of his references to go to the constants class also.

Now for a change

As we add more and more configuration values throughout development, it becomes clear to our team that the "storeOptions" section name has become a little misleading and we want to rename the section to "storePaymentOptions".  Since Joe's assembly references our configuration assembly, all we need to do is change the constant value, rebuild, and redeploy, right?  Wrong.  When we redeploy, Joe's assembly (which he can't rebuild yet for some organizational reasons) breaks, and still references "storeOptions".  How is this possible when both of our assemblies are pointing to the same value?

Compile time versus run time

The problem is that the value of constants are determined at compile time.  That means that the value for the constant is actually substituted in the IL during compilation, so the final IL code never references the original constant.  If I change the constant value, every single assembly referencing the constant will need to be recompiled.  That's not what I'd like to do.  I just want to change the value and have all dependent assemblies pick up the new value.

Instead of using a constant, let's use a static field instead:

public static class ConfigurationSections
{
public static readonly string StoreOptions = "storeOptions";
}

Static field values are determined at run-time instead of compile time.  I can change the value of the static field, only recompile the configuration assembly, and now all dependent assemblies referencing my static field will get the correct value!

Conclusion

So the moral of the story is to always use static fields instead of constants.  The compile-time nature of constants is one of those "gotchas" that can really kill you with crazy errors at deployment time.  Additionally, static fields should be named using PascalCasing.  Constants should be reserved for values that can and will never change.

Thursday, June 14, 2007

VSTS Scrum process templates

There have been some rumblings around that some in my company might be interested in Scrum and Team System, so I thought I'd compile a list of Scrum process templates and some highlights (and lowlights).  The three Scrum process templates I've found are:

Each process template adds custom work items and reports related to Scrum, but they all have their quirks and niceties.

Scrum for Team System

This process template was originally released a year ago by a company called Conchango.  You can find this template on the Scrum for Team System website at http://www.scrumforteamsystem.com/.  I've personally used this template for about 10 months covering about a dozen Sprints.

Pros

  • Great website with thorough process guidance and free training videos
  • Mature, with several updates to the template
  • All sprint artifacts present, with automatic rollup calculations
  • Good reports, including:
    • Sprint Burndown
    • Product Burndown
    • Product Backlog Composition
    • and about a dozen more
  • Portal reports, a set of smaller reports designed for the project SharePoint portal
  • Support through forums
  • Widely adopted
  • Includes tool to update warehouse (critical for up-to-date reports, as Team System only updates the warehouse ever hour or so)

Cons

  • All artifacts created and managed through Visual Studio, which not all team members may have
  • Reports have a lot of custom code, making them difficult to tweak
  • Does not plug in to the Areas and Iterations constructs already present in Team System
  • Only one active project per Team Project
    • i.e., everyone in the same Team Project will use the same sprints, work items, etc. with no good way to partition them
    • This forces every new team to have a new Team Project

Microsoft eScrum

This one was just released from Microsoft, and from the description, looks like it's been used internally at Microsoft.  I found this on a post on Rob Caron's blog.  That post links to a download on Microsoft's downloads site here.  I should note that I tested all of these process templates using a Team System VHD, so I didn't have to get access to our corporate Team System server.

Pros

  • Fantastic web portal for managing sprints and sprint artifacts.  It's all Ajax-y too.
    • Pages for managing daily sprints, reports, etc.
  • Context-sensitive help in web portal
  • Dynamic capacity calculations in portal
  • All sprint artifacts present
  • Ability to have multiple "Products" in one Team Project in source control, allowing multiple teams to use Scrum for one Team Project
  • Allows definitions of each role (Project Member, Product Owner, etc.)
  • Some better options on each work item type, such as categories
  • Integrates with Areas and Iterations
    • Areas are Products
    • Iterations are Sprints

Cons

  • New, released only on June 12
  • No support through forums, or anywhere else online (Google only found 2 relevant pages)
  • Painful setup, lots of manual steps
  • Not as many reports (~half a dozen)

VSTS Scrum Process Template from CodePlex

I also found this one on the post on Rob Caron's blog.  At the bottom of the post, it links to the CodePlex project.  This project was intended to improve on the Scrum for Team System process template by taking advantage of Areas and Iterations.  It's being developed by a handful of TFS MVP's.

Pros

  • Lightweight, fits in well with Areas and Iterations
  • Good list of reports, some of them quite different than the other templates
    • Unplanned work
    • Quality Indicators
    • Project Velocity
    • Builds
  • Supports basic Scrum/Agile work items (User Story, Backlog Item, etc.)
  • Custom work item for reviews
  • Open source, so it's updated frequently

Cons

  • Open source, so don't look for great support
  • Still in beta
  • Not a lot of people using it
  • No project portal
  • No installer

Summing it up

The Conchango process template is by far the most mature, so I'd usually go with that one, but the awesome portal site and the integration into Areas and Iterations make the eScrum process template a compelling alternative.  As for the CodePlex template, it looks promising, but I'll reserve judgement until a final version is released.  Doesn't look ready for prime time quite yet.  The great thing about process templates is that you can edit them after you create the Team Project.  If there's a report missing you want, it's pretty easy to look at one of the other process templates and see what others are doing, and add whatever you need.

Wednesday, June 6, 2007

Team Foundation Build, Part 4: Values, Principles, and Practices

In the last post of this series, I discussed how to create a build definition. Before I start discussing extending and customizing Team Foundation Build, I think it's important to discuss some values, principles, and practices regarding automated builds and continuous integration. To establish some context, I suggest reading the original Martin Fowler article on CI (Continuous Integration).

When looking at extending a build, it's difficult to see which direction to go without having a target or destination in mind. The idea behind having values and principles is to create a shared target that the team tries to hit. I'm paraphrasing Kent Beck and Martin Fowler quite a bit, so apologies in advance if these ideas are old news to you.

Values, Principles and Practices

Values and principles are a set of ideas that a team believes are important or worthwhile. By themselves, they can be vague as they aren't specific towards a specific domain. But without clear values and principles, the practices the team tries to enforce will have little or no meaning. Values and principles establish a context and meaning to practices, and practices are followed to reinforce a set of values and principles.

Practices are a concrete set of actionable items. They are either done or not done, and there is no gray area. Whether or not you have followed a practice is very clear. A set of practices go hand in hand with values, as Kent Beck says, "Just as values bring purpose to practices, practices bring accountability to values".

Values and Principles

So if a team decides that it is important to have values regarding builds and integration, what should these values be? I've suggested a few, but these could be expanded and modified depending on the context of the team. It's not important that these specific values be agreed upon, but that the team communicates and agrees upon some set of values.

Feedback

The only constant of software development is change. Change comes in many forms, whether it is requirements, environments, personnel, etc. It then follows that the software itself is in a constant state of change. Developers are modifying code, adding features, fixing bugs, refactoring, simplifying. But how do we know if the changes we're making are successful or even correct? Change necessitates feedback, and feedback validates changes (or invalidates).

Change happens on a constant basis, so feedback should happen early and often to handle the constant changes. The longer we wait to receive feedback on changes, the more difficult it will be to decipher the feedback. The shorter the feedback loop is, the greater chance we will get meaningful and actionable feedback.

Simplicity

Simplicity is far more difficult to achieve than complexity. However, complex systems take more time to understand and change than simple systems. The simpler the system is, the easier change becomes. Since change is constant in software, optimizations should be made towards simplicity over most anything else.

Maintainability

Simplicity leads to greater maintainability. A maintainable system is a system that is easy to change. Maintainability can be achieved through simplicity, clarity, and solubility. If I can look at a piece of a system and understand its role or behavior in less than ten seconds, it's highly soluble, and therefore maintainable. If a codebase has a solid suite of unit tests with high coverage rates, the codebase is maintainable because the unit tests enable change.

Practices

Practices are the kinds of things you do day-to-day. They're not really in place as a set of goals themselves, since enforcing the values is our true goal. It's important to always keep the core values in mind when following the practices, and sometimes it doesn't make sense to follow all of the practices. But understanding the values behind the practices will provide context for discussing whether or not to follow each practice. The following build practices are designed to reinforce the values and principles defined above.

Single source repository

A single source repository is the single truth of the state of the system. Developers can be editing code on each of their systems, but having a single source repository enforces a single point of reference for keeping and finding source code and other information.

Single source repositories don't have to only store code, nor can source repositories be only code repositories. SharePoint is a type of repository that can store documents. When you store a document in SharePoint, no one argues which email attachment document is the correct document from whatever email you received. There's only one document, and one place to find it.

Automate the build

Human intervention is a typical source for errors in manual processes. To me, automation is just another way of eliminating duplication in processes. I don't want to duplicate my manual actions every day if I can have a reliable way to automate it. I'll probably screw something up. The more a process is automated, the less likely it is to fail. We're also less likely to forget to do the manual process in the first place. Builds can be complex, and with rich build tools available in nant and MSBuild, there's no reason not to automate the build.

Self-testing build

So we've automated our build, and our code compiled without errors. But is that sufficient for a successful build? In addition to merely compiling the code, our build should also run tests against the compiled code for further verification. Does our code meet specifications and requirements? After all, two developers could check in code that compiles successfully but blows up at runtime, or doesn't pass customer acceptance testing. By having a self-testing build, we can receive deeper feedback from our builds.

The tests your builds can run can vary depending on how often the builds run. I posted a while back on classifying tests, and when looking at what tests to run, the length of time it takes to run your tests is generally proportional to how often you want to run them. If it takes 3 hours to run a test suite, you can't run these tests in a build that builds every hour.

Everyone commits every day

The longer you wait to commit changes, the more difficult it will be to integrate those changes. Let's suppose I'm changing module A that depends on module B. While I make my changes, developer Joe makes several changes to module B. He adds a couple of features, changes some behavior, maybe even modifies the interface of a couple of classes that I use. If I make a 100 changes in a week, that's a lot of changes that could potentially break because of Joe's modifications. Maybe it's only the changes on the first day that broke, but now it's Friday and I don't remember exactly all of the changes from the first day.

The longer I wait to elicit feedback, the less useful that feedback becomes. If I commit daily, the most amount of changes I need to worry about is only what I did yesterday.

Every commit should build

Following the message of getting feedback early and often, I should build every time I check in to get feedback from the build. If I have an automated, self-testing build, it would be easy to set off a build often. If I commit a dozen changes, and then build, I have a lot more to worry about if the build breaks or a test fails. It could be any one of those dozen changes that broke the build, and it's up to me to wade through them all to figure out which one. I should instead add one change at a time, incrementally adding functionality and getting feedback from the build that I haven't broken anything.

I like to compare this to the construction of a building. The builders don't wait until the entire building is finished to see if it's built right, they make small changes and additions, measuring and verifying as they go, until the structure is complete. If they wait until the building is finished, it's several magnitudes of order more expensive to fix problems that could have been caught early on.

Keep the build fast

Also known as the 10-minute build rule. If every commit sets off a build, I don't want to have to wait to get feedback. Any longer than 10 minutes might mean I've moved on to something else, and lost the internal stack of ideas in my head that I used to build the original change. What ends up happening in reality is that teams have several types of builds. Usually there is a "CI build" that runs on every commit, and it only builds and runs unit tests. The longer the build takes to run, the less often the build should execute. A deployment build that runs lengthy regression tests could run nightly.

I don't like to be kept waiting for feedback. If I ask someone a question in a conversation and they wait 5 minutes to answer, I've already walked away. Builds are the other entity in the daily conversation of development, always answering the question, "Is what I just checked in correct?" The quicker I get the answer, the quicker I can move on.

Everyone can see what's happening

The entire team should be aware at all times of the statuses of the build(s). Since the build output is the final result of the team's production, it's in the team's best interest to keep the build "green", or successfully run. When it's easy to see the status, you can start putting some rules in place:

  • The whole team drops everything to fix a broken build
  • Nobody checks in when a build is broken or in progress
  • Nobody leaves after checking in a change until the build is green
    • In other words, don't check in something and go home without verifying the build is green

When results and status are visible, the team accepts responsibility and becomes accountable for the build. If no one can tell what the status of the build is, no one will care.

Automated deployment

Manual deployments seem to be one of the biggest headache-inducers in development. Any manual process, no matter how explicitly defined in a Word document or Excel spreadsheet, is inherently error-prone because it requires human action on each step. Humans are...well, human, and mistakes will happen. Having an automated deployment eliminates the human error of a manual deployment.

You can also go one step further and have a self-testing deployment, running the same tests that the builds executed. Having a automated, self-testing deployment would save countless hours of time spent diagnosing and troubleshooting deployment problems. Deployments are stressful enough, I'd like to have some confidence in what I'm deploying with an automated, self-testing process.

Summing it up

By themselves, each practice is valuable. Together, the build practices multiply their collective value significantly. To introduce these practices, always start with automating the build and work your way down the list. It's not very effective to have a self-testing build if the builds aren't automated, as adding tests to a manual process would just make life more difficult and this practice would likely be dropped. Automation is a great enabler, as it allows much richer and complex processes to be possible.

I should also point out that these values, principles, and practices should be discussed and agreed upon in the team, not dictated to the team. Ideas agreed upon are much stronger than rules forced upon. In the final post in this series, I'll look at customizing and extending Team Build to enable you to follow these build practices.

Tuesday, June 5, 2007

ASP.NET 2.0 Page Lifecycle Poster

I found this cool poster that details the ASP.NET 2.0 page lifecycle, and it includes the Page, Control, and Adapter events.

As complex as the lifecycle is becoming, this one's definitely going on the wall. Also note that since Page inherits Control, all of the Control methods will fire for the Page as well. For more information about the ASP.NET page lifecycle, check out the MSDN documentation.