次の方法で共有


D3: Building Great Software is a Battle, Don’t Leave Any Assets on the Sidelines

Another D3 release is up.  This time I continued my focus on fundamentals.  Some of this is tedious, but as I establish solid mechanics I’m starting to gain some momentum.  The theme for today is using every last asset at your disposal to fight the battle to build great software.  When I first came to Microsoft a number of years ago, I was given the book Writing Solid Code to help get me on the path to building world-class, reliable software.  While the book is a bit dated these days, the key message I took away is as important to me now as it was then:

Writing software is hard.  So hard, in fact, that we need to play every trick in the book (and then some) if we’re going to have any hope of really getting it right.

The previous two versions of DPMud were just hacks, but this time around I’m trying to build something that demonstrates how the EF can be used as a key component in software to be proud of.  So, I ought to do things right—at least as much as I can given time constraints and everything else. 

Often Overlooked Asset #1: Analysis Tools

This weekend I spent some time enabling fxcop and stylecop for D3.  Actually I’m using the VSTS code analysis feature rather than fxcop because it’s built in to the VSTS download of VS2010 with really nice integration.  Turning it on was a breeze, I just right clicked on each of my projects, chose properties, clicked on the code analysis tab and then checked the Enable Code Analysis on Build box.  Actually, the first step was to download and install the VSTS flavor of VS2010 since I had initially just installed VS-Pro.  I was happy to find, though, that the web install was able to install VSTS on top of my existing pro install without any trouble and was even smart enough to skip all the minor components that were already on my machine…

CodeAnalysis

The hard part was discovering just how many issues it could find even with the very small amount of code present in D3 so far.  As you can see above I’m just using the minimum recommended rules right now, and even with that I had to fix quite a few issues.  Most of them were essentially cosmetic things, but it did catch a place where I was doing string concatenation of a SQL query that should have been using a parameter.  There wasn’t any user input in sight, so I didn’t really have to worry about an injection attack, but it’s still a much better practice to use parameters wherever possible so I was happy to fix it.  At some point I expect that I’ll go back and crank the rule-level up even higher, but there were enough issues when I did that which were more about personal preference than really being super important (like requiring that assembly names begin with a capital letter) that I decided it wasn’t worth the effort just yet.

I also downloaded stylecop and turned it on.  It’s a little more work since it’s not part of the core product, but like fxcop it really was surprisingly easy to turn on and then the real work was making the project comply.  I’m so glad that I did it now rather than later in the project, because keeping things going as I make incremental changes appears quite easy—the hard part is in the conversion.  If you have a large amount of code already, I would still recommend making the switch to these tools, but you might want to checkout the stylecop team blog where there is a post about ways to make sure all new code is checked and then gradually convert older code.  Fxcop is probably a bit more difficult but still worth it.

In order to turn on stylecop, I copied the binaries for the tool to a directory under my source control, modified the batch file I load for my development environment to set an environment variable pointing to the directory where stylecop is present.  Then I unloaded each csproj, edited the XML and added one import element pointing to the stylecop targets file (I put mine next to the Import element for the standard csharp targets):

 <Import Condition="'$(StyleCopPath)' != ''" Project="$(StyleCopPath)\Microsoft.StyleCop.targets" />

I put the condition attribute on it so if you don’t have the environment variable defined, it won’t try to load anything.  That way everything builds and works without stylecop.  (Code analysis also has the feature that if you don’t have it installed because you have VS-pro or something instead of VSTS then the configuration is just ignored and everything builds fine.)  Unfortunately, this same edit needs to be made to each csproj file so the trick is to make sure not to forget this when adding new assemblies in the future.

By default both code analysis and stylecop report things as warnings, but I wanted to make certain everything was fixed so I also added a couple properties to the csproj files that change them into errors rather than warnings:

 <CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<StyleCopTreatErrorsAsWarnings>false</StyleCopTreatErrorsAsWarnings>

One interesting issue that I ran into is that the stylecop VS integration doesn’t seem to work for VS2010.  Most of the time that’s fine because the VS integration is really only about editing the stylecop settings to turn off certain checks and the like.  The build integration that I describe above is all you need to make sure your source code is checked and warnings or errors appear in the VS IDE, but if you do have something you want to turn off (and in my case I decided to turn off the requirement to add documentation comments to everything as well as a few other small cosmetic things) then you need to edit the stylecop settings.  Fortunately if you double click on a Settings.Stylecop file in windows explorer, it will launch the UI for editing the settings independent of VS.  Probably there’s some other way to bootstrap this process, but for me the easiest way was to just launch the VS integration in VS2008 and then copy the settings file to my other project.  Once you have the right settings file, you just need to put it in the root directory of your project.

Asset #2: Check Constraints in the DB

I don’t recommend trying to add check constraints for everything, but there are places where the database is in a good position to enforce constraints on your data.  In many cases the EDM itself will enforce constraints—in the case of D3, the association between Actor and Room has cardinality 1:* rather than 0..1:* which means that every Actor must have a Room, and the EF will enforce that.  In the case of the association between Item and Room and the association between Item and Actor, we need there to be one and only one of those associations for each Item at any one time, and the EDM doesn’t have any way to represent that.  For both of these kinds of issues we will want to add business logic to the classes to make sure these things are prevented well before the time of saving changes, but it’s still good to enforce them in depth.

 

In the case of the item associations, I decided to use a check constraint in the database.  So I added a partial method call to the generated database creation code so that it’s easy to do this:

 internal partial class SqlDb
{
  partial void Customize()    
    {        
       // Check Constraint which requires that Items live in either a room or on an actor.        
     this.ExecuteCommand(@"ALTER TABLE [dbo].[Items]
                           WITH CHECK ADD CONSTRAINT [CK_Item_one_and_only_one_location]
                           CHECK ((([Room_Id] IS NULL OR [Actor_Id] IS NULL) AND
                               NOT ([Room_Id] IS NULL AND [Actor_Id] IS NULL)))");
       this.ExecuteCommand(@"ALTER TABLE [dbo].[Items] CHECK CONSTRAINT [CK_Item_one_and_only_one_location]");
 }
}

Which brings me to the last asset in my list for today.

Asset #3: Unit Testing Your Brains Out

I’m sure this isn’t anything new to most of you, but like many others I’d say that I probably can’t emphasize enough the impact really drinking the unit testing kool-aide can have on your development.  D3 truly doesn’t have all that much code yet, and every time I add more tests I discover more issues.  Testing makes your code better as you go, plus it creates both documentation and automated verification which will help prevent regressions later.

Conclusions

There are always things you can do to make your code better.  Even more important, though, is to find things you can do to make CERTAIN that your code is better and will stay better.  Your software will benefit from it—D3 already has.

- Danny