Shaking Things Up: Scrum, Agile, and TDD
In mid September I started working as a vendor for Microsoft. The majority of my time involves coding. Prior to September I had only a vague idea of what scrum and test-driven development were. Scrum aside, I just couldn't understand why anyone would want to spend time writing all these tests and THEN code their application. My philosophy was, "Let me get some code together that needs to be tested. THEN I'll write the tests." Of course, as you've probably already guessed, the tests were seldom written.
I've learned a great deal since that September day, and I continue to do so. The entire project I'm on follows agile methodologies, uses scrum, and is coded by way of test-driven development. It was a shock to my system. Not all software development at Microsoft is handled this way, incidentally. I consider myself one of the lucky ones to be exposed to it.
I come from the "old school" way of thinking. That is, project managers and business analysts would spend months and months (if not years) writing up specifications and project plans. More frequently than not, no development was done until the business owners signed off on these specifications. In addition, because very little (if anything) functional was put in front of the stake-holders during this entire time, the specifications were frequently revised and edited many times, to the point where they were often vaguely identifiable from their originals.
This is the type of management approach that caused the project at my last employer to fail. The client demanded that full specifications be completed as one of the requisites of my employer getting paid. The development team would start to implement as much as we could due to pending deadlines - only to find obvious holes or errors in the logic once we put usable features in front of the people in the middle of writing the specs. We were literally developing to a moving target. One of the systems, a workflow API, was redesigned four times because the requirements kept changing as we delivered what was documented. So we were faced with pending deadlines to deliver functional specifications for several modules as well as pending deadlines for development - in parallel. You do the math. Because the director had staffed up so much to get all these specifications done (for a while there were 15 people on the PM team), and because it took almost a year, the money ran out.
Now, with that in mind, imagine a methodology whereby you could put useful functionality in the hands of the stake-holders on a regular basis (perhaps monthly) right from the start. Each month you deliver functionality, and you get instant feedback on that delivered functionality so you can make it better the next month while you're continuing to build the system. By stake-holders, I mean the actual client - the people you're building the system for, not the project managers or business analysts caught in the middle who are trying to gather requirements.
This doesn't solve the problem of some clients requiring large functional specification documents, but it does offer at least one potential change to the way they're written: the functional specification can be written AFTER the majority of functionality has been developed and delivered. This is a huge step toward an accurate specification and it also drastically reduces the amount of time it takes to write the document. In addition, every month the development team is getting direct input to keep the application's business usefulness on track. Granted, there are cases in which business owners will not release budgetary dollars for development until these specifications are signed off, but that is a case for another blog entry.
When I was reading about scrum and agile methodologies I found myself approaching it with skepticism. I was so used to spending the time to create complete logical and physical models before a line of code was written that I just couldn't understand how anyone could possibly set all that aside and only code what was required for that particular month. It only took about two months for me to truly see how this new approach works so much better for everyone right from the start. The hardest part is often convincing the die-hard old-schoolers that it's time for a change.
With the help of Doug Seven, Brad Wilson, and Scott Densmore I am also now a big proponent of test-driven development. For those who are unfamiliar, here is a simple example.
I was asked to create new methods on a class, and these methods would allow someone to add and delete a shortcut from one group to or from another.
With test-driven development, you write your test first, then you write just enough code to make the test pass. The theory is that you could spend hours, weeks, or months trying to account for every possible (real and theoretical) scenario as you design an object model or database. Instead of spending so much time on that, you write some tests that validate the one immediate goal you have.
To fulfill the requirement given to me, the first thing I typed was the following code:
[TestMethod]
public void AddShortCutWithTwoExistingIdsReturnsTrue()
{
Guid toGroupId = new Guid("{45722B64-0354-4ed4-A813-1BD67926EAA0}");
Guid foreignGroupId = new Guid("{F4B729C4-B922-42bc-BED9-C70D9F0A8C36}");
bool result;
result = disco.AddShortcut(toGroupId, foreignGroupId);
Assert.IsTrue(result, "AddShortcut failed.");
}
// AddShortcutWithNonExistentToGroupIdThrows
// AddShortcutWithInvalidToGroupIdThrows
// AddShortcutWithInvalidForeignGroupIdThrows
// AddShortcutWithNonExistentForeignGroupIdThrows
// DeleteShortcutWithNonExistentFromGroupIdThrows
// DeleteShortcutWithInvalidFromGroupIdThrows
// DeleteShortcutWithNonExistentForeignGroupIdThrows
// DeleteShortcutWithInvalidForeignGroupIdThrows
// DeleteShortcutWithTwoValidIdsReturnsTrue
I came up with 10 unit tests, all of which validate 2 little methods – AddShortcut and DeleteShortcut. Now it’s my job to write one test, as I did above, and then write just enough code to get that test to pass. The following is actually too much (all I needed was a method that returned true, but I thought that would be too simple for this example):
public bool AddShortcut(Guid toGroupId, Guid foreignGroupId)
{
if (toGroupId == new Guid("{45722B64-0354-4ed4-A813-1BD67926EAA0}") &&
foreignGroupId == new Guid("{F4B729C4-B922-42bc-BED9-C70D9F0A8C36}"))
{
return true;
}
return false;
}
Note the hard-coded Guids. That is because I don’t need any more code in order to make the test pass. As I write the other 9 tests, my code will look more and more like you’d be used to, but before I would have spent possibly an hour thinking about how I wanted to implement these two methods. Instead, in one hour I have all my code written, and there are at least 10 unit tests that validate that the code works the way it should.
A side effect of this test-driven development is a specification for my method - in code. In other words, someone who needed to write a spec could examine my tests and gain a huge amount of information to accurately document what this particular method of the class does.
Now consider the effect on the project if every developer did this, especially in a scrum / agile environment. We have just a couple dozen classes in our project and well over 300 unit tests. I can now feel much more confident in updating my code (or adding new code) because I have the ability to run all unit tests that were written by all developers on the project to insure I haven't made any breaking changes.
Thanks to my new team of mentors for teaching me this. It's funny how in just a couple of months I'm at a point now where I shutter to think of writing code without having a test that I'm trying to satisfy first!