Tuesday, October 9, 2007

Are Story Runners appropriate?

Scott recently voiced his opinion on the validity of story runners (i.e. the xBehave tools) in an agile shop.  First, let me say that I sincerely appreciate the passion Scott has for BDD, and it's that passion that will drive the community forward.  Second, in recognizing that passion, I won't respond to comments about technology fetishes and code-sturbation for now, but I do honestly understand the concerns behind them.

Scott sees a tool in search of a problem, and in completely disagreeing with the problem it's trying to solve, questions the motivation of those developing the tool, which he sees as seemingly selfish and egotistical reasons.  From that point of view, again, I completely understand Scott's concerns, and luckily I have a Bellwarese translator to get to the heart of those concerns.

I did feel it unfortunate that the BDD conversation revolved around tools.  BDD is far too nascent to argue tooling, and instead the discussion should have focused getting the language, values, and concepts right.

So Scott's core question is:

Are Story Runners appropriate for executable requirements?

As an aside, not once in my experience have I had a BA come up to me and say "hey, while you're delivering this, it would be great if you had some automated tests for it too".  I've never had anyone from the business ask me to use any kind of automated testing tool, such as NUnit, FitNesse, NSpec, or NBehave.  These tools will always be pushed by geeks, as it is the geeks that see the value of these tools first.

For me personally, capturing stories in code was never about traceability.  Specifically, the tangible benefits Joe and I saw were:

  • Providing a more complete description of the behavior of the system
    • Unit tests too granular
    • Even specifications can be difficult to organize
  • Stories provide a better overall, macro view of the system
  • Executable stories remind us of our final goal
    • For us, the final goal isn't the unit tests, but that the story is satisfied.
    • Capturing in code helps direct us towards that final goal
  • Executable stories can help capture the conversation
  • Executable stories can help shape our domain model and infuse the ubiquitous language into the system

The more appropriate venue for our discussion was Jeremy's topic Sunday on executable requirements.  Stories are part of BDD, as we can all agree, but should stories be used as executable requirements?

The result from the discussion was "let's see, as nothing else has worked great so far".  Start with stories, end with stories.  Stories -> Scenarios or Aspects -> Specifications (NSpec) -> Unit tests (NUnit) -> Integration Tests (FitNesse/NUnit) -> Functional/Acceptance Tests (FitNesse/NBehave).

I do appreciate this dialogue, as only through debate with the truly passionate can clarity be achieved.

 

 

p.s. please don't suggest exploding laptops, we've had enough problems with that here...

Monday, October 8, 2007

ALT.NET Impressions

The ALT.NET Conference is over, and I'm exhausted.  I've never been to a conference that consistently challenged my assumptions about software development.  The amount of dialogue and debate that occurred was quite staggering, considering that most involved were birds of a feather, and the possibility of echo-chamber issues would seem to be fairly high.

Open Space Format - Day 1

The conference was run in an Open Space format, which I've never been involved with before.  No agenda was set before the first day, and no one was obligated to stay for an entire discussion.  The conference started with a few ground rules and an explanation into Open Spaces, along with a short roundtable about the direction and purpose of the ALT.NET conference.

Between five and seven chairs were placed in the middle of the room.  If you wanted to address the audience, you got up, sat in the chair, and said your piece.  If you sat in the last available chair, someone else in the panel sat down.  One chair was always available for someone to claim, and no one could talk in the audience.  This provided a great continuous discussion for which anyone in the room could participate.  Great questions and discussions centered around "What is ALT.NET?", "Is ALT.NET divisive?", "Is ALT.NET negative?", and many others.

After this initial dialog, we proceeded to create the agenda.  Those who wanted to talk about something wrote it down on one of the many post-it note pads scattered around the room, walking to the center of the room, and telling everyone what they wanted to convene about.  Then they would put their post-it on an open space in the schedule at the front of the room.  A long line formed for those proposing discussions next, and each put their post-its in an open slot in the schedule.  When the spaces were filled, these post-its were put below where they could be sorted later.  After all those that wanted to convene a discussion were through, all audience members were invited up to the schedule to initial post-it notes they found interesting.  There around 6 or so rooms to convene discussions during each session, and all were filled by the end of the night.

Quite frankly, it was a astonishing that we went from no agenda to a very compelling one in about an hour with little or no planning or contention.  But then again, this was my first Open Space conference.

Day 2

The next morning, the schedule had gone through an iteration where many topics were consolidated, some were moved and some were removed based on the voting the previous night.  Scott Guthrie's MVC presentation was pushed back to accommodate the most audience members.  No one needed to commit to any one topic per session, as they could use their own two feet to go to a different discussion.

First Session - Spreading passion and DSL's

During the first session, I mostly attended a discussion concerning the problems of spreading passion and learning throughout the organization.  Jean-Paul had some insight on inspiring passion, while Scott Hanselman mostly talked about making ALT.NET tools accessible.  Dave Laribee talked about the importance of instilling values, but there was still a lot of negative vibes around the Morts.  Mort's shouldn't be shunned or disdained, they should be embraced and included.  The rest of the discussion concerned how to include Mort in the ALT.NET community.

Next, I headed to the DSL conversation that had folks like Martin Fowler, Scott Guthrie, Jeremy Miller and others.  I only caught the tail end of the conversation, but Martin talked a little bit about the difficulties of fluent interfaces, the reasons behind them (no one knows yacc), and Scott mentioned that debugger visualizers can work around many of the pains of fluent interfaces.  Fluent interfaces (as I ran into with NBehave) run into the issue where a continuous chain of method calls, no matter how you format it in the code, still turns into one line of executing code.  If something fails and throws an exception, you don't know what line it happens at and you can't step into a specific method call.  You have to step in and out of each fluent interface method call to get to whatever problem you have somewhere down the chain.

Martin again pointed out that this wouldn't really be an issue if we just used true external DSLs and used tools like yacc or Boo to create them.  Once you do that, however, you do lose much of the debugging support from Visual Studio.

Second Session - Intro to Boo and the BDD gauntlet

I hadn't used a lot of Boo, and was very interested in the parsing and extension platform Boo provides.  I've seen some work done on a Boo build system (BooBS, and don't search for that one).  That looked very intriguing to get rid a lot of the executable XML we have around in NAnt, MSBuild, XAML, Spring, and so on.  Unfortunately, the discussion mostly covered the language features of Boo and not some of the more interesting core features of the Boo compiler, so I moved on to another session.

One of the newer topics in our space was BDD, which Scott Bellware convened a session on.  However, when I walked into the room (with about 80 or so people in it), I saw Joe Ocampo in the center of the room and NBehave story definitions up on the projectors.  Joe was taking a lot of heat from those in the audience questioning whether stories were BDD, whether executable stories were a good idea, and so on.  I felt pretty rotten about the situation, as I was not around to help Joe explain our goals with NBehave.  But I bought him a couple of beers that night, so I guess it evened out.  I understand and even embrace Scott's (Bellware) concerns that executable stories can hamper the conversation that a story is supposed to represent.  But Hanselman came to the rescue to explain to Scott that these types of requirements and traceability are critical to many people, and those concerns shouldn't be dismissed out of hand.  Personally, I think if NBehave doesn't provide value, it doesn't have a reason to exist.  However, what's valuable varies widely from team to team.

I would have rather this discussion concern BDD than NBehave, since BDD is a new an interesting topic that isn't widely understood.

Again, sorry Joe, my bad!

Third Session - Scott Guthrie is an MVC ninja

The third session mostly consisted of Scott's presentation on a new MVC framework in development at Microsoft, due to be released as a beta in the next couple of weeks.  Much has already been discussed about the specific architecture, so I won't go into that.  My specific impressions were:

  • I've never EVER heard "Rhino Mocks", "StructureMap", "NUnit", "Spring", or any other major OSS tool that the community uses mentioned in a presentation about MS technology.
  • I've never EVER seen an MS presentation actually USE one of these tools.  Scott used NUnit to write tests for the controller before he ever created the view, which made me shed a single tear.
  • The first points always brought up were support for swappable, testable, mockable architectures, which the new MVC framework supports inside an out.  There wasn't a scenario that was brought up that wasn't already available.
  • For the first time in .NET, it seemed that MS listened, engaged, and adapted to feedback from the community to influence its decisions and core architecture of the framework.  Testability, mockability, and embracing other OSS alternatives were first-class citizens in this brave new paradigm.  Bravo!

Fourth session - Hanselman knows IronPython (kinda) plus some DDD-jitsu

The next session mostly consisted of Scott Hanselman showing IronPython and IronRuby running on the MVC framework.  Questions kept coming up about "will MS run RoR?", but the answer was always "if the community wants that, they can do it, the tools are there."  Scott showed IronRuby on ASP.NET MVC, not RoR ... on CLR ... on IIS.  It was all very interesting how he was able to get IronPython running on the MVC framework, and he showed us that it didn't really take that much to get it up and running.  Most of the talk was about the differences between the dynamic type model of the DLR and the static Type object of the CLR, and how to resolve the two.  In the MVC framework, you specify that "I want to execute this view of Floogle type", but Floogle type doesn't exist yet since it's a dynamic type.

I popped into a talk about MbUnit and xUnit.NET, got bored, and checked out another on DDD-jitsu.  The DDD talk was interesting, as the core concepts were laid out and much discussion talked about how to introduce and implement these ideas.  Dave Laribee stressed the importance of the later chapters talking about large-scale structures and bounded contexts.  This pretty much ended the day

Later activities - Hanselman is a rock-jockey

I went to dinner at Chuy's (love that Tex-Mex) with about 20 folks including Jeffrey Palermo, Martin Fowler, Scott Guthrie, Scott Hanselman, about 10 other people.  It was rather strange to eat next to the voice of Hanselminutes, which I've listened to for so long.  Scott Hanselman gave me a great tip on mapping directories to shares, to make my life easier with Windows Home Server.  Scott doesn't seem to like cats too much either...  I also realized how much diabetes can affect someone's daily life as Scott had to stop what he was doing and self-administer an insulin injection at the end of the meal.  That was a good time for Scott to mention his Diabetes Walk 2007.  It was amazing to me that Scott can be as successful as he is, even though this disease consumes so much of his daily life.

After dinner, most of the group headed to Main Event, which is similar to Dave and Busters or Jillian's.  Main Event has rock climbing, which all of us were eager to prove our mettle on.  It was cool to see Scott Hanselman coach several of us up an intermediate course and ring the bell at the top.  What was even weirder was it was only Scott's first or second time to try rock climbing, and he was a complete natural.  The rest of the night was spent at the pool tables with a few beers and a lot of great conversation.

It was a very long day, compounded with the fact that most of it was spent learning, debating, absorbing, arguing, and just trying to keep up.  It proved very hard to sleep with so many conversations still buzzing around my head.

Day 3

The final day started with some breakfast and a meeting outside to remind us all of the rules.  Outside was a good idea, because no one looked very alert.

First Session - Bringing ALT.NET to the masses

This one was more of a continuation from the previous day's "passion" talk, but with some folks from the MSDN magazine and architecture journal from MS.  It was another great discussion with lots of ideas to bring ALT.NET to the masses, including:

  • Expanding the ALT.NET website to include a wiki, videos, and more introductory material with links to more detailed information (mostly to Ayende's blog)
  • Creating demos or a starter kit that installed NHibernate, NUnit, CC.NET, and the full OSS stack locally.  Basically, rewrite the PetStore starter kit to use ALT.NET ideas to provide a nice introduction and example application
  • Aligning the PnP group with the other events.  We all know that agile stuff is happening inside MS, but we never hear about it at the MSDN events.

There were several other ideas, but I realized afterwards that a nice bound notebook is a great note-taking tool, better than "maybe I'll remember".

Second Session - Executable requirements nirvana - StoryTeller and NBehave

This talk started with Jeremy Miller discussing the problems and difficulties of FitNesse, such as:

  • Great for tabular data-driven tests, but not great for complex models
  • Difficult to integrate with source control
  • Breaks often with refactorings, as everything is string-based (similar to the problems with NMock)

He showed StoryTeller, along with some tests.  It made running and authoring FitNesse integration tests much easier, but still had the issues that FitNesse has.

Next, Joe and I gave a demo of NBehave, as well as a good discussion on where its value is.  Mainly:

  • It gets the language right
  • It integrates well with automation
  • It runs complex objects well
  • It has a very grokkable interface

However, there were some shortcomings in that NBehave doesn't do well with tabular data, and much has to be copied and pasted to do so.  Jeremy suggested an integration with StoryTeller and NBehave, or FitNesse and NBehave to allow users to run story-based integration and acceptance tests.  At one point, Joe thought it was a good idea to show the code behind NBehave.  It was too late to take the beers back that I had bought him the night before.

Closing the space

At the end of the conference, everyone gathered in the main room, and all had a chance to give share their final thoughts.  It was an amazing experience, and even Scott Bellware got a little choked up at the end.  We went to lunch afterwards at Saltgrass Steakhouse, where Roy Osherove and Rod Paddock shared a story about a dirty joke, which we found could be applied universally to any situation.  The story itself is NSFW, so I'll just have to relate it later.

Final thoughts

This was by far the best conference I've attended, I can't wait for the next one.  I left excited and energized, glad to be around so many people with so much passion.  The conversations were always engaging and inviting, and never closed or exclusive.  Any one could jump in to any talk at any time, and challenge any assumption of the discussion.  I think the ALT.NET community (if that name does indeed stick) proved that there is a vibrant community in .NET that can provide conversation and feedback to MS, and not just a lot of external, negative noise.  While I mentioned a lot of names here, I wasn't trying to name-drop but to show that this community is accessible and eager to engage with others.  This was an important first step in creating a healthier, more inclusive community, and I hope it isn't the last step.

Friday, October 5, 2007

Compiler warnings from generated code

Although I believe strongly in treating warnings as errors, on rare occasions I get compiler warnings from generated code.  Examples of generated code include the designer code files for Windows and Web forms, XAML, etc.  Warnings in those files are easily removed, as it's almost always related to files coded by the programmer.

I recently hit a really strange compiler warning while using the aspnet_compiler tool, which compiles the ASPX, ASCX, and other content.  Part of this process is to parse the ASPX and ASCX files and create C# files from those.  However, I started getting very strange warnings from the precompilation:

c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
  Temporary ASP.NET Files\ecommstore\5c1cb822\
  6aeabbae\App_Web_d0wxlov2.10.cs(87):
  warning CS0108: Ecomm.Login.Profile' hides inherited member 'Foundation.Core.Web.PageBase.Profile'. 
  Use the new keyword if hiding was intended.

Shadowing warnings aren't new to me, but this one is especially strange since it came from ASP.NET.  Specifically, a property created in an auto-generated class conflicts with a property in a global base Page class we use for all of our ASP.NET pages.  The base Page class is created by another team here, which provides all sorts of logging, etc.  I can't touch that one, nor would I want to, as it is used company-wide.  That base Profile property isn't virtual either.

But how can I get rid of the other one?  I tried all sorts of magic:

  • Shadowing and exposing as virtual (the subclass and override method)
  • Shadowing and preventing overriding by using the new and sealed modifiers on that property

Nothing worked.  No matter what, the Profile property would get created.  What does that code file actually look like?  Here's the more interesting part:

public partial class Login : System.Web.SessionState.IRequiresSessionState {        
    
    #line 12 "E:\dev\ecommstore\build\Debug\ecommstore\Login.aspx"
    protected global::System.Web.UI.WebControls.PlaceHolder mainPlaceholder;
    
    #line default
    #line hidden
    
    protected System.Web.Profile.DefaultProfile Profile {
        get {
            return ((System.Web.Profile.DefaultProfile)(this.Context.Profile));
        }
    }
    
    protected ASP.global_asax ApplicationInstance {
        get {
            return ((ASP.global_asax)(this.Context.ApplicationInstance));
        }
    }
}

There's the offender, the auto-generated Profile property.  Looking back at some older build logs, I notice that this warning didn't show up until we migrated to ASP.NET 2.0.  One of the new features of ASP.NET 2.0 is the Profile properties.  ASP.NET 2.0 profiles allow me to create strongly-typed custom profiles for customers and let them be integrated into our ASP.NET pages, be automatically stored and retrieved, etc.  If I defined custom properties for my profile, then a dynamically created Profile class would be used (instead of the DefaultProfile type).

However, we don't use Profiles, so I can just turn them off in our web.config file:

<profile enabled="false" />

By turning Profiles off, the Profile property is never created in the auto-generated code.  The warnings go away, and our problem is solved.

There are several other instances of these new ASP.NET 2.0 features in auto-generated code causing naming collisions with existing properties when migrating from ASP.NET 1.1.  The solution was always to rename your properties, but since I couldn't do that, turning Profiles off did the trick.

Thursday, October 4, 2007

Treat warnings as errors

Compiler warnings can provide some additional insight and quality controls on your codebase.  They can tell you about obsolete code, unused variables, and many other items that you wouldn't necessarily see on visual inspection.  Warnings can also surface bugs, such as possible null reference exceptions, or expressions that always evaluate to "true".

However, compiler warnings can be easily ignored.  Ignore them for long enough, and important warnings can be lost in a sea of "acceptable" warnings.  For higher quality code, you can treat warnings as errors.  When a compile time warning is found, the compilation fails.  Warnings also have varying severity, from "0" (basically off) to "4", which includes all warnings.  Set your warning levels to "4" to get the most mileage.

To turn on "Treat warnings as errors", go to the property pages for your a project and click the "Build" section.  In the section "Treat warnings as errors", set it to "All" and set the warning level to "4":

If you use the aspnet_compiler tool or Web Deployment Projects, you can also turn on "Treat warnings as errors" for these pre-compilation steps in the web.config file (.NET 2.0 in this example):

  <system.codedom>

    <compilers>

      <compiler

        language="c#;cs;csharp"

        extension=".cs"

        type="Microsoft.CSharp.CSharpCodeProvider, System,

          Version=2.0.3600.0, Culture=neutral,

          PublicKeyToken=b77a5c561934e089"

        compilerOptions="/warnaserror"

        warningLevel="4"

      />

    </compilers>

  </system.codedom>

The compilers section allows me to fine tune my ASP.NET compilation options, including warning levels and treating warnings as errors.

By treating warnings as errors, I can start dialing up the quality of our code a little at a time, producing a more maintainable codebase.

Wednesday, October 3, 2007

Daily routine with continuous integration

I chuckled quite a bit after reading the Top 5 Signs of Discontinuous Integration, though I think "dysfunctional integration" is a better word.  So what's my routine?

Start of the day

  1. Check if build is green
  2. Get latest if it is
  3. Fix build if it isn't

Coding

  1. Code/write tests
  2. Run local build, make sure code compiles and tests pass
  3. Get latest
  4. Run local build again
  5. Check in, with decent comments
  6. Wait for CI build to finish
  7. If build is red, drop everything and fix
  8. Otherwise, go back to step 1

I'll get latest several times per day.  The more often I integrate (the "continuous" part), the easier it is do so.  If you're scared to get latest version when the build is green, you probably have some dysfunctional integration issues.  If you don't know what "the build is green" means, well, that's a whole other ball of wax.

Tuesday, October 2, 2007

The Legacy Code Dilemma and compiler warnings

I hit the Legacy Code Dilemma today while trying to reduce compiler warnings in our solution.  For those that don't know it, the Legacy Code Dilemma is:

I need to fix some legacy code, but the fix isn't ideal.

After working on green-field development for a while, dealing with legacy code can be frustrating at first, unless you go through Michael Feathers' excellent Legacy Code book.  Whenever I feel the need to spend two days refactoring a small area to get it under test, I have to remind myself that it's more important to deliver business value AND pay down technical debt than paying down technical debt alone.  But then you hit dilemmas, where you KNOW the solution you're putting in is ugly, but just don't have the resources to "do it right".

To fix or not to fix

I hit that today, where I was trying to reduce compiler warnings.  I could fix the compiler warnings by making them go away, or fix the design problems that made the compiler warnings show up in the first place.

For example, I encountered a lot of "variable declared but not used":

try
{
    return PricingDatabase.GetPrice(itemId);
}
catch (Exception ex)
{
    return 0.0M;
}

In this case, the specific warning is "The variable 'ex' is declared but never used".  The problem is the variable "ex" is declared but never referenced in the "catch" block.  The design flaw, at least one of them, is that exception handling is used for flow control.  Any time you see try-catch block swallow the exception and do something else, it's a serious code smell.

The problem about fixing the design issue is that I'm dealing with legacy code, which has no tests.  Such design changes are foolish and even dangerous without proper automated tests in place.

Live to fight another day

If I wanted to fix the design flaw, I was in for a real battle.  I would need to get this module under test, and get any client modules under test.  For a codebase with little or no unit tests in place, that's just not feasible.

Instead, I'll fix the warning:

try
{
    return PricingDatabase.GetPrice(itemId);
}
catch (Exception)
{
    return 0.0M;
}

I kept the exception swallowing and all its nastiness, but gained a small victory by removing a compiler warning.  Since this solution has several hundred compiler warnings, all these little victories will pay off.  Instead of charging off on a path of self-righteous refactoring, I've fixed the pain and lived to tell the tale.

Monday, October 1, 2007

Setting off a CC.NET build from NAnt

One of the new features in CruiseControl.NET are integration queues with priorities.  By default, all projects can execute concurrently, but sometimes, I want to have a master build execute first, then dependent builds next.  This is fairly easy to do, using the integration queues, priorities, and force build publishers.

However, I have a strange situation where I actually need to force a build from a NAnt script (don't ask).  There aren't any command-line tools to execute CCNET builds, so I couldn't use the <exec> task to accomplish this.  Instead, I'll create a custom NAnt task.  To do this, I'll need to create a new C# project and reference three key assemblies:

  • NAnt.Core.dll
  • ThoughtWorks.CruiseControl.Core.dll
  • ThoughtWorks.CruiseControl.Remote.dll

From there, it's just a matter of creating a custom task to call into the CCNET API:

[TaskName("ccnet")]
public class CCNet : Task
{
    private string _server;
    private int _portNumber = 21234;
    private string _project;

    [TaskAttribute("server", Required = true)]
    public string Server
    {
        get { return _server; }
        set { _server = value; }
    }

    [TaskAttribute("portnumber", Required = false)]
    public int PortNumber
    {
        get { return _portNumber; }
        set { _portNumber = value; }
    }

    [TaskAttribute("project", Required = true)]
    public string ProjectName
    {
        get { return _project; }
        set { _project = value; }
    }

    protected override void ExecuteTask()
    {
        RemoteCruiseManagerFactory factory = new RemoteCruiseManagerFactory();
        string url = string.Format("tcp://{0}:{1}/CruiseManager.rem", Server, PortNumber);
        ICruiseManager mgr = factory.GetCruiseManager(url);

        string proj = mgr.GetProject(ProjectName);

        mgr.ForceBuild(ProjectName);
    }
}

The custom NAnt task needs at least two pieces of information to connect to the CCNET server:

  • Server name
  • Project name

If the remoting port is different than the default (21234), I'll need to specify that as well.  Now I can execute a CCNET build with this simple NAnt task:

<ccnet server="buildserver" project="Ecommerce CI" />

I'd still rather use the integration queues, priorities, and publishers, but the NAnt task will work for me in a more complex scenario.