5/10/2008

Wrangling Rhinoceri

I've been asked to help some teammates become more comfortable with Rhino.Mocks. I'll practice my explanations here, and y'all can offer guidance and ask clarifying questions if you're so inspired. I'll be glad for the help.

Rhino-what? What-mocks? What-what?
The domain we're discussing is unit testing, the code that developers write to verify the logic in their code. Rhino.Mocks is not a testing framework, but a mocking framework, which means it allows you to mock (simulate) parts of your application. You use it along with a testing framework like NUnit or MbUnit.

If you have written code without keeping an eye on testability, it is likely to be witheringly difficult to apply Rhino.Mocks after the fact. Life is simpler if you write the tests first, therefore Rhino.Mocks plays well with Test-Driven Development, which drives you to design your code in a testable way.

As far as what it is, Rhino.Mocks is a dll you include with your test project files. You create a reference to it from your test project, and it gives you some additional classes and methods you can use within your unit tests.

State-based and interaction-based tests
There are two main styles of unit tests, state-based and interaction-based, and each is useful for different things. With state-based, you set up some variables, run them through the methods you want to test, and then verify that their state has become what you expect. You use Assert statements to assert, "The state should be [this]. If not, fail the test." If you're testing an addition method, you can verify the state of the answer, as in, "I assert that 2 plus 2 should be 4."

Rhino.Mocks is used in interaction-based tests. Here you are verifying the interaction between your classes. For example, in a Model-View-Controller architecture, you could test your Controller's interactions with the Model and the View, as in, "I want to verify that, when the Controller is asked to display customers, it gets customers out of the data repository, runs the results through my CustomerFormatter class (I don't know, it's just an example), and sends them to be displayed on the user interface."

You have three interactions there, so you'll probably write three tests. You are not testing what comes out of the data repository, just that the Controller talked to it. In fact, you don't even need a real data repository for this test. If you could just simulate that repository in a way that let you verify it was called... This is what Rhino.Mocks gives you.

What next?
Martin Fowler has written a great article, Mocks Aren't Stubs. If this is your first introduction to Rhino.Mocks and mocking frameworks, perhaps not everything in that article will click with you. It is still worth reading now, and I'll link to it again in my next post. It wouldn't hurt to read Martin's article twice.

My next post will include why you would want to simulate classes instead of using the real thing. I also want to explain why you even want interaction-based tests. I have an intuitive sense of the value, but I am not yet good at articulating it. Feel free to chime in.

Labels:

5/09/2008

Globally distributed scrum

I'm on record as saying that globally distributed scrum teams can work, but sub-optimally. I'd like to retract and revise that position, so that something I'd said doesn't inadvertently lead a team down a poor path.

My team's scrum adventure lasted from March 2007 to April 2008. (Why does it have an end date? Buy me a beer, and we'll talk. But none of the people who were a part of or were affected by our efforts wanted it to end. The project was a success.) We started out with four developers in Austin, six developers in Bangalore, a Test Lead in Austin, and five testers in Hyderabad. I'll call this the Two Parallel Teams period. Team members were moved over time, so that we became two developers in Austin, eight developers in Bangalore, a Test Lead in Austin, and three testers in Hyderabad. This is the One Distributed Team period.

We had a good collection of team members, and I think we could have worked together very effectively, had the distance between our desks not been so great. I am not criticizing off-shoring; I am criticizing scattering your product owners, developers, and testers across different continents. Note who I included in that list there; if your dev team is co-located, but your product owner is far away, you have a distributed team.

We did not have a choice about where our teammates should be located, but we had influence over which project methodology we would use. So the choice was not "distributed scrum versus co-located scrum," but instead "distributed scrum versus distributed waterfall."

Do I still think distributed scrum is better than distributed waterfall? Yes, although with less unbridled enthusiasm. It required more meetings from all team members (including product owners) at crazy hours, but it still made it easier for the product owners to get the system they need; it still had us writing more code, more often, which is way more fun than fighting over contracts; and it still enabled productive, visible progress toward our goals.

We were happiest in our Two Parallel Teams mode. The developers in one city worked on one feature, and likewise for the other city. The only people who really suffered here were the product owners and the scrum master, who had to accommodate a ten-and-a-half-hour time difference to have their sprint planning meetings.

We offset our two-week sprints by one week, so that the testers could be shared between the two sets of developers. It worked okay because it did not require the "save state and hand off" process inherent in distributed teams. That process is, at the end of the day, you write an email explaining what you did, where your thoughts are about the work-in-progress, what you want your teammates to pick up, and what you want them to leave alone because it's currently too fragile to explain or share. It's like "hibernating" in Windows--save a snapshot of where you're at--and it takes time.

The more distributed the team, and the more they need to collaborate, then the more time will be spent handing off, and the team will be less efficient. In our One Distributed Team mode, the hand-off process overwhelmed the development process, so that more time was spent saying what you're doing than doing. This is the dangerous mode I want to warn people away from. If multiple people are collaborating on a feature, they have to be able to talk to each other, in person, real time.

There are still dangers lurking in the Two Parallel Teams mode. The most insidious is the way it undermines egalitarian, democratic, self-organizing decision making. For a team to decide things like designs, system architecture, even working practices, they have to be able to discuss and debate as a team. This requires trust, camaraderie, and personal safety. Those things are built in informal, day-to-day interactions--y'know, the way friendships are built. Over email and conference calls, they happen incredibly slowly, if at all.

You have to be comfortable with someone to be able to argue with him. The threads stitching together a global team are tenuous, and we operate with great delicacy to avoid snapping them. The communication channel is so anemic, you have to choose every word carefully, to ensure it does not accidentally offend. This is the opposite of having the confidence to know that, after we argue about this topic, we'll still be a team. Which means a lot of decisions go un-debated; they get made by default instead of by consensus.

Do I recommend globally distributed teams? No. Regardless of the project methodology, and no matter how talented the team members are, you will be dramatically less efficient if the members of your team are scattered over the planet. If you're off-shoring, then off-shore--put developers, testers, product owners, project managers, the whole team in one place. Software development is intrinsically collaborative. Structure your teams so that they may collaborate.

This experience gave me a taste, however, of how fun scrum can be. With a co-located team, you could seriously rock and roll.

Labels: ,

5/02/2008

Software Development as Manufacturing Metaphor

Alistair Cockburn's talk the other night was inspiring, thought-provoking, and entertaining. If you have the opportunity to hear him speak, I highly recommend it.

I have been wary of the agile community's current enthusiasm for the Lean literature, as applied to software development. I'm hesitant to trust "manufacturing" as a metaphor for "software development," because I work for a large manufacturing firm. My executive management believe they are experts in efficient manufacturing, and therefore apply those lessons to managing software teams. The agile-to-lean trend gives them permission to make this leap.

The problem comes in attending to the wrong part of the metaphor. Incorrectly applied, it overlooks that developers are knowledge workers, and you are led to believe that they are interchangeable laborers who should specialize on a small task. Easily outsourced, among other things.

But Mr. Cockburn made me realize the right part of the metaphor to apply, the useful part. He said, substitute "unverified decisions" for "inventory," and now you can map your team's process, look for bottlenecks, and improve throughput. When unverified decisions pile up, work is not getting done.

Mr. Cockburn said that different types of bottlenecks mean you need different solutions. He also flouts the strictest adherence to Lean, which would have you remove all waste, and instead suggests spending efficiency to increase throughput. You have too many developers and too few db designers? Let the devs iterate through a bunch of designs and code until they have it really baked, before handing it over to the db designer. Too many analysts and not enough devs? Get the requirements solid, signed off, and thoroughly detailed before giving them to the devs. Too few product owners to tell you which route to build? Build both and let them choose.

Spend your excess efficiency in order to increase the speed of the overall team.

Back to my beef with the manufacturing metaphor. Incorrectly applied, it lets you think waterfall software development makes sense: They give us the specs for a computer, we build it, it gets tested. But the scale is wrong. What a waterfall project actually does is give you the entire order for the DoD's workstation upgrade initiative, and you build 1000 computers before any of them get tested. (A huge backlog of unverified decisions.) Rapid iterations bring you back to building one computer at a time. And Scrum makes the deciding and the verifying nigh simultaneous...
Developer: Purple or green?
Product Owner: Purple.
Developer: 2gigs or 4?
Product Owner: Ooh, 4!
Tester: Oops, hold it, you actually grabbed the 2.
Developer: Whew, good catch.

What I learned from Mr. Cockburn is that the Lean philosophy holds useful lessons for the agile practitioner. Drawing an analogy between writing software and installing chips on a motherboard does not help you, but thinking of your software team as a process comprising different roles, each with their own decisions to verify, can help you increase your team's productivity.

4/28/2008

Computer as Brain Metaphor

I have been splitting my reading lately between finite automata and metaphors that shape thought and action. My husband Jonathan and I have an on-going debate about the appropriateness of using the computer as a metaphor for the human brain. I find it useful for describing all sorts of events; he feels it undervalues the capabilities and elegance of the brain.

I probably don't need to pitch my side of the argument to y'all. You've likely said things like...
  • I wish I could install more RAM.
  • Dude, that guy totally overclocked his processor.
  • I can only hold x things in memory.
  • Core dump!


But it bothers Jon, and he takes me to task whenever I say something that betrays my underlying metaphor. I've been trying to understand why we have different emotional stances on this, and I've hit on the following. As a programmer, I think of my computer as the outlet of my creativity, the tool by which I express my craft. A user tends to think of his computer as an annoying appliance that would give him access to all this great stuff (entertainment, social contact, information, pr0n), if it would just freakin' work. You've seen how users treat their laptops: bang, pound, whack! Given how Jon and I view our brains (as valuable, precision tools), I can see why he does not want to think of his brain the way a user thinks of his computer.

Beyond that, I see Jon's side: This metaphor leads you to think of intelligence in terms of processor speed or amount of RAM, which leads you to believe you can quantitatively compare the intelligence of one human to another, and that leads to a very narrow definition of human intelligence. There is more to intelligence than number of instructions per second. To satisfy me, a metaphor for intelligence has to accommodate empathy and personality and intuition. From these spring creativity, and society, and humanity.

At least, that's what the wetware wants me to believe.

4/18/2008

Runs in the Family

I'm a language geek, but I try not to be a nit about it. I like the subtle nuances in choosing the "right" word for a situation, but not at the expense of connecting and communicating with real human beings. However, sometimes a small change in word choice can have a large impact on clarity.

In JP's Nothin' But .NET class last week, we had an object called "criteria." (He was creating a DSL for generating SQL statements. Neat stuff.) My brain kept tripping over the example, until I realized, "Oh! Your criteria is a single thing, and I'm expecting it to be a collection. Ah ha!" So I suggested a name-change to "criterion," not to be pedantic (I swear), but just to improve clarity.

A little time passed, and then a student raised the objection I was expecting. "Well, actually, Merriam-Webster's says..." I know this objection well; I've explored it, and I've read Merriam-Webster's editorial philosophy (Did you know your dictionary has an Introduction?). M-W has a descriptive focus, not prescriptive; their intent is to capture and document language usage in the wild. It's a good resource, but not when you want to know the "proper" use of a word.

Knowing this did not improve my image in the class.

Ah, well, I gotta be me. Teasing me, one student said, "Hey, you know so much about dictionaries. Do you have strong opinions on hash tables?" No... that's my dad!

Labels: ,

4/05/2008

NAnt and LINQ - namespace error

"The type or namespace name 'Linq' does not exist in the namespace 'System' (are you missing an assembly reference?)"

I was getting this error when using a NAnt script to build a project I created in Visual Studio 2008. Turns out to be related to which .NET Framework is targeted, in this case.

Aside: If you are not using NAnt, and you're getting this error when you try to build, then make sure your project has a reference to the System.Core assembly. But if you use the defaults in Visual Studio 2008, then you already have this reference. My project would build through Visual Studio, but not through my NAnt script.

Here's the cause. I thought I should use the latest stable release, instead of the latest beta, so I downloaded NAnt 0.85. It supports multiple frameworks, and by default it targets the framework in use. You can point it to another using the -t command-line argument.

But NAnt 0.85 is aware of frameworks only up to 2.0. For LINQ, you need 3.5, and therefore you need NAnt 0.86 (in beta at the time of this writing). I kept 0.85 just in case, so my NAnt folder contains a folder for each version; they co-exist. I just changed the path in my nant.bat file to point it to 0.86-beta. And voilà.

Hope this helps. Yay, automated builds!

Labels:

4/02/2008

Pretty code: Skeptical of elses

Pretty code is readable code. One strategy for code beautification is to look critically at every if/else statement. Is there a more streamlined way to write that statement? Cultivate a general mistrust of elses.

Some examples...

Testing a boolean to return a boolean

if (x == true)
{
  return true;
}
else
{
  return false;
}

becomes

return x;

If you need to swap those,

return !x;


That's a pattern. Recall that comparison operators (less than, greater than, equal to) return a boolean. So this also applies here:

if (myPropertyToCheck == someValue)
{
  return true;
}
else
{
  return false;
}

becomes

return myPropertyToCheck == someValue;


Guard clauses
You can use a guard clause when you are testing if it is safe to do something, and if not, you want to exit.

if (safeToDoThis)
{
  DoThis();
}
else
{
  return;
}

becomes

if (!safeToDoThis) return;
DoThis();

When the DoThis() is rather involved, guard clauses greatly help the readability for your future teammates (even when that's you). Step 1, check if we're in an okay state, and if not, just get out of there. This saves you from the Hunt The Else game (although, if that matching else is that hard to find, you could break up your method into smaller pieces with more specific responsibilities).

Comparisons
Compare() and CompareTo() methods need to return -1, 1, or 0. It's handy that these are integers, because now you can harness the Power of Arithmetic to do your bidding. Also when you are comparing your own classes, you often want to compare a property that is a value type or a string. Those already have their own CompareTo() methods, which you can borrow.

Say you want to sort your children by their ages. You don't need

if (child1.age < child2.age)
{
  return -1;
}
else if...

(Not just an if/else, but an if/elseif/else. Aaah!) Use instead:

return child1.Age.CompareTo(child2.Age);

If you want to sort them from oldest to youngest, this is where the arithmetic comes in. To flip a negative 1 into a 1, multiply it by negative 1. Same for flipping positive 1 into a negative 1. And zero, conveniently, doesn't mind being multiplied by anything. So you could say:

return -1 * child1.Age.CompareTo(child2.Age);

You can get it even simpler. Because -(-1) = 1, and -(1) = -1, your Compare() method becomes:

return -child1.Age.CompareTo(child2.Age);

Not only no elses, but also no ifs! Very pretty.

Reducing the number of paths through your code (i.e., reducing cyclomatic complexity) gets it closer to being read like prose. Simpler code has fewer bugs, and your successors who have to read your code will think you are smart and good looking. Keep a healthy mistrust of else statements, and write pretty code.

Labels:

3/02/2008

5 Qualities that Make Social Software Social

I have single friends who try to meet people by going to bars. But the only thing you have in common with people you meet this way is that they are trying to meet people. Contrast this with taking a class, joining a club, of volunteering for a charity, where you meet people with a common interest and compatible outlook. Much more likely to hit it off.

Social software intrigues me, as it ushers us further down our path to becoming integrated cyborgs. What creates self-sustaining communities? What causes some corners of digital life to reach critical mass and become essential social outlets, while others wither and wander off? What makes these sites successful?

MySpace and their knockoffs strike me as attempts to meet people in a bar (complete with the assault of unwelcome music when you walk in the door). Flickr (photos) and Folia (gardening) are clubs for people with similar interests. I have fun hanging out in these clubs and enjoy the people I meet there. The first community-builder is: Pull together people with a common interest.

Closely related but slightly different is that hobby-oriented sites give people something to talk about besides themselves. For a while I used LiveJournal to keep in touch with my friends, until I got completely fed up with it. I like people better when I am not privy to their every insecure and narcissistic thought (and they shouldn't be subjected to mine, either). But when I keep up with my friends via Flickr, I see their projects, their trips, their outings and their adventures. Those are excellent conversation-starters. The second community-builder is: Plant conversation seeds, something to talk about or argue about that is outside the realm of psychotherapy.

Something that delighted me about Flickr from the first time I used it is the human-oriented, whimsical language displayed by the software. The site greeted me in a different language each time I logged in—how silly! When they brought their new messaging system online and I received my first message, I said, "Hey! A message!" I clicked on it, and Flickr displayed on the screen, "Hey! A message!" That this "photo management system" would talk to me like a goofy, light-hearted friend charmed me utterly. The third community-builder is: Set the tone; make it a fun place to hang out.

If you've played with Flickr or YouTube or LibraryThing, you've had occasions where you go to look up one little thing, which causes you to stumble onto another thing, which leads you to another thing, and then you look at the clock and find that hours have elapsed, making you wonder if you were abducted by aliens who then wiped your memory. By allowing you to stumble upon content, following tangentially related linkages, these sites invite you to explore. The exploration engages your curiosity and keeps you there for hours. Unexpected discoveries create a feeling of delight. The fourth community-builder is: Enable serendipity.

I have been puzzling over my own site, Invisible City, for years. My husband has posted a free print-and-play board game every month since 2000. We get an encouragingly consistent number of hits. The site has areas for visitors to comment, and yet... hardly anyone does. It's like performing a show every night to a sold-out crowd who never claps. The site is definitely missing something.

Looking again for lessons from Flickr, Folia, and YouTube, I have a theory: Community members need to establish their own identities and create their own contributions. In other words, they need a profile page and a place to post their stuff. Invisible City is more like a gallery than a community, because people can come comment on our content, but they can't display their own. Galleries provide a useful service, so there isn't necessarily anything wrong with Invisible City's format, but it will never become a hip hang-out unless it changes. The fifth community-builder is: Give members the means to showcase their distinct identities.

Meat-market social networking sites aimed at helping singles hook up will always be transitory, passing in and out of popularity like fads. A community-oriented site can grow and blossom into a self-sustaining organism with staying power. Looking at my own habits and preferences as a user, I observe that the following five facets facilitate the formation of communities:

  1. Provide services and features that pull together people with a common interest.

  2. Give them something non-narcissistic to talk about.

  3. Set the tone to create a fun place to hang out.

  4. Enable exploration and serendipity.

  5. Give members the means to showcase their distinct identities.

Do you have a fun site that you love to hang out in? What makes it fun and what keeps you coming back?

1/25/2008

Past the Summit

Earning the "instigator" lable in my blog masthead, I collaborated with some fellow employees to present the "Agile Summit." Yesterday was the day. It was an internal event, a day of training to give a taste of the concepts—whetting the organization's appetite.

The day was definitely a success, but I am also relieved to be past it. (You need a good waterfall project, with lots of slack, to have the time to plan such a thing.) My product owner and I were also part of the agenda, as a case study of one of the few teams at our company using an agile method. Given only a few minutes to get my message out to 300 people, I chose to focus my talk on collaboration. If people learned only one thing from me yesterday, let it be this:

Talk to each other.


Don't use documents and processes as walls to hide behind. Don't look to Agile to be new wallpaper for those old barriers. Agile is a different mindset, and tenet number one is collaboration. Break down barriers and get rid of gate keepers. Here are some of the compelling benefits we've experienced.

Between the business and developers:

  • We're able to react to changes (or new information) in the business process, and we're always working on the highest priority features.
  • Prototypes—or, really, quick shells of working code—clarify requirements. It's easier to communicate what you want when you have something to look at.
  • Developers can understand the why of what they are building, which lets them build the right thing.
  • As the business gives feedback and sees that feedback incorporated into the code, they get more engaged and give more feedback. It's worth their bother.

Between the testers and the business:

  • Testers are able to understand the business reasons and user goals.
  • Testers will ask questions from a user perspective ("Wouldn't the user want to do this?"), which makes a better product.
  • Here's a quote from one of our India-based testers, when asked about the advantage of our project's collaborative model: "Everyone in the team has the freedom to ask questions and concerns to the Business." Freedom. Right on.

Between the testers and the developers:

  • This code is more tested, period, than on any of my past projects.
  • Developers get feedback the very next day on code they have just written.
  • Testers are aware of changes in requirements and logic because they are attending the stand-up calls. No need for change requests and updating specification docs.
  • Visibility (in the form of the burndown chart) gives us a real, honest feel for where we are in the project timeline. You can look at it and really know whether we're on time or not.

This collaboration makes us successful, and it makes the work more fun. I mean, which would you rather do: talk to a teammate or fill out a 20-page template?

Labels:

1/12/2008

Dictionary as DropDownList Data Source

I have a Dictionary<T,T> called myListOptions. To use it as the datasource for an ASP.NET DropDownList called MyList...

MyList.DataSource = myListOptions;
MyList.DataTextField = "Value";
MyList.DataValueField = "Key";
MyList.DataBind();


It took me some rummaging to realize that you use the actual strings "Value" and "Key," so I hope this post comes in handy for someone else in the future (even if it's me).

Labels: