How Not To: Write Documentation

Check out this “How to” over at MSDN. Be sure to look at the code example. You mono guys can go to. I’ll wait.

Ok, did you laugh?

It is as if the code example wasn’t even looked at by someone who can program!

I’m still laughing about it.

I think I’ll write my own similar documentation.

public class HowNotTo {
WriteDocumentation(That,Does,Not,Even,Compile);
ShootSelf(InTheHead);
}

Us vs. Them Round 20070828

There is a very interesting discussion going on over at Sasha Sydoruk’s blog. The signal to noise ratio is surprisingly high for the subject matter being discussed.

Sasha posed the question asking where are all the cool and new web sites that run ASP.NET. Other than MySpace, which moved from ColdFusion to ASP.NET a few years ago, and really isn’t that cool or new anymore, why are all the other “cool” sites running some other technology?

Reading this discussion is the first time I’ve actually missed doing the other the “P” or Rails type web development. I was starting to see some of my old feeling in the comments posted. I haven’t been all that impressed with webforms in asp.net for quite sometime.

MonoRail is pretty damn cool. In fact, MonoRail is so close to a “P” or the “R” type web development experience.

I think maybe that I just miss web development a bit. Which is insane! I hate web development. It is a huge pain in the ass. JavaScript is a pain. CSS is nice and all, but I find it painful. Maybe I find it painful because I never properly mastered it so I always have to reference docs to see if I have selectors right. But no. I think it has more to do with the fact that it would be insane to not continue to target IE6, since it is still the most widespread browser, yet it is nasty, nasty, nasty to try to use nice web features like CSS2 and CSS3 which are supported by Firefox and Opera, which will never be supported by IE6.

Maybe it is because “P”, the “R” and even Webforms are slowly becoming irrelevant on the UI side as Flash via Flex and Silverlight become more capable of building great web applications instead of just great games. On the back end side I know what development experience I like. It has little to do with Visual Studio and more to do with C# and CodeRush. Maybe I’m too tied to C#.

 I must say that I certainly think in C# these days. That is, when I look at python list comprehension, generators and even ruby blocks, I immediately think of them in terms of C#. C# can do all those things and they are actually very similar to how it is done in python or ruby. What I’m not very good at is thinking of a nice internal business-case DSL in C# and thinking about how it would look in Ruby or Python. I think this is my current thought limitation. I hope the discussion I read over at Sasha’s has caused me to find a place where I can learn and grow.

Exception Handling 101

There is a lot of very good documentation on how to use Exceptions in .NET. If you don’t want to read these, and you are a programmer and unsure of how to use Exceptions, when to catch them, when to throw, or anything else surrounding the topic of exceptions, my recommendation is for you to do nothing.

The Must Reads are these:

  1. Best Practices for Handling Exceptions http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconbestpracticesforhandlingexceptions.asp?frame=true&hidetoc=true
  2. Error Raising and Handling Guidelines http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp?frame=true&hidetoc=true

 

This is a case where doing nothing is better than doing the wrong thing. If you REALLY feel like you should not crash, and continue if possible, and you just want to display a Message Box that says “ERROR” with a Red X then I give you these lines of code.

static void Main()

{

System.AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

...

}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)

{

    MessageBox.Show("An error has occurred.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);

}

static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)

{

    MessageBox.Show("An error has occurred.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);

}

 

Please please please don’t write code like this

 

 

public static void PleaseDoNotCodeLikeThis()

{

    try

    {

        if (batchFileName.Length > 0)

        {

            if (batchArchiveFolderName.Length > 0)

            {

                if (File.Exists(batchFileName))

                {

                    try

                    {

                        DoSomeStuffWithBatchFile();

                    }

                    catch (Exception ex)

                    {

                        throw (new Exception(“Error in batch file. “ + ex.Message));

                    }

                    string sWorkPath = TMP_DIR_PATH;

                    DoSomeOtherStuffWithResultsWhichMayNotExist();

                    if (Directory.Exists(batchArchiveFolderName))

                    {

 

                        sArc = batchSingleRow.outputFileName;

                        sArcPath = batchArchiveFolderName + Path.DirectorySeparatorChar;

                    }

                    else

                        throw (new Exception(“Invalid batch archive folder name.”));

 

 

                    foreach (object myObject in someCollectionOfJunkFromThisBatchStuff)

                    {

                        DoEvenMoreStuffIncludingForkThreadsWriteFilesAndOtherStuff();

                        try

                        {

                        ParsingAndDoingOtherStuffWithFiles();

                        }

                        catch (Exception ex)

                        {

                            DoStuffJoinThreadsCloseFilesWriteErrorLog();

                            continue;

                            //this.Close();

                            //return;

                        }

 

                        DoStuffJoinThreadsCloseFilesWriteErrorLog();

 

                    }

                    if (cnt == batchDataCollection.Count)

                    {

                        WowWeWereCountingAndItLooksLikeWeGotItAllBuildAMessageAndDisplayItToUserOhWellIfItWasNotAllDoneWeLeaveTheUserHanging();

                        MessageBox.Show(arcBatMsg, “Archive”, MessageBoxButtons.OK, MessageBoxIcon.Information);

                        this.Close();

                    }

                }

                else

                {

                    MessageBox.Show(“Error! Invalid batch file specified. “, “Error”, MessageBoxButtons.OK, MessageBoxIcon.Warning);

 

                }

            }

            else

            {

                MessageBox.Show(“Specify a folder for storing the new archives. “, “Error”, MessageBoxButtons.OK, MessageBoxIcon.Warning);

            }

        }

        else

        {

            MessageBox.Show(“Specify a valid file for archiving in batch mode. “, “Error”, MessageBoxButtons.OK, MessageBoxIcon.Warning);

        }

    }

    catch (Exception ex)

    {

        MessageBox.Show(“Batch Archive not created. “ + ex.Message, “Error”, MessageBoxButtons.OK, MessageBoxIcon.Error);

    }

}

If this code looks familiar to you, then you have my deepest sympathy.

If you aren’t sure what is wrong with code like this, then please, seek help.

I should be clear. Each of the longly named methods like DoSomeStuffWithBatchFile and DoSomeOtherStuffWithResultsWhichMayNotExist are of my own creation. This is where I removed between 50-200 or maybe more lines of code from this method. Yes, this is one method. The above has a Maintenence Complexity of 218. The original which I gutted has an MC of 1657. It isn’t even the highest MC in the application. I don’t have to look at the 2188 scored method that is a Windows Form clicked event handler.

 

Current Frustration Level: Moderately High

Current Amusement Level: Very High

Current “Wow, now I understand the frustration of others” Level: Record High

Anyone can write software. It probably won’t be maintainable.

A Proposal to Revise Maintenance Complexity Ideal Score Table

When Mark Miller introduced his new Maintenance Complexity metric, I wasn’t immediately in awe. I thought I would prefer Cyclomatic Complexity. I was wrong.

I’m looking at a small project with a couple of simple bugs which was thrown at me yesterday. I’m recognizing the “Ultra-complex method. Extremely challenging to maintain. High priority for simplification” interpretation of the 1000+ MC score.

Mark’s original table is pretty good.

MC Score Interpretation
<= 100 Simple method. Easy to Maintain.
101-200 Medium method. Relatively easy to maintain.
201-300 Large method. A little more challenging to maintain.
301-600 Complex method. Strong candidate for refactoring.
601-1000 Very complex method, challenging to maintain. Should be broken down.
1001+ Ultra-complex method. Extremely challenging to maintain. High priority for simplification

 

I propose a revision to the table.

MC Score Interpretation
<= 100 Simple method. Easy to Maintain.
101-200 Medium method. Relatively easy to maintain.
201-300 Large method. A little more challenging to maintain.
301-600 Complex method. Strong candidate for refactoring.
601-1000 Very complex method, challenging to maintain. Should be broken down.
1001-1499 You may want to find the person who wrote this and invite them to your local .Net User Group. Buy them Object Oriented Programming for Dummies. Make them read Code Complete. Make them browse, read, and submit patches to known good open-source projects. Try your damnedest to make them understand single responsibility and separation of concerns principles.
1500-1999 Suicide might cross your mind. You KNOW that it would be better to scrap this bit of code and rewrite, but then you would have to take responsibility for it and you would be stepping on peoples toes. You want to find the person who wrote this and strangle them and ask them WTF they were thinking.
2000+ Go postal. Find the original author and smash their fingers until they look like Sparks’s fingers in Der Dieb. Optionally treat them like Sparks’s until “It hurts really bad where…”. If this is foreign to you, study up on Sealab 2021.

 

If anyone could help me with other interpretations of high MC scored methods, I would appreciate it.

WCF Service Host implementers beware

Jon Flanders has a great and very detailed post about implementing your own ServiceHost by extending ServiceHostBase instead of ServiceHost when using WCF.

He points out what I believe to be a serious flaw, not with the design of WCF and ServiceHost, but rather the implementation of the two. It turns out ServiceHostBase relies on specific behavior of ServiceHost. This is a typical OO no-no. Jon describes it very well and calls it a “magic method”. I just call it a mistake.

It is disappointing to think that there was not a CanInheritFromServiceHostBase test case out there in WCF-land somewhere. <snarky>I guess it was too difficult to write with mstest, and they weren’t allowed to use NUnit or MbUnit</snarky> 🙂

update: A link to the post would be helpful. 🙂

Ben Scheirman hates MSTest too!

I’m not the only one!

http://feeds.feedburner.com/~r/flux88/~3/141244484/DearVSTSTestIHateYou.aspx

Ben, since MSTest‘s functionality dwarfs that of NUnit’s I’ve found it relatively easy to have most of my tests run in both MSTest and NUnit. Of course, where I used NUnit’s more advanced functionality, I just disable the test in MSTest. Yes, its kind of a hack, but it works.

Each of my test files have using statement sections like this:

#if NUNIT
using TestClassAttribute = NUnit.Framework.TestFixtureAttribute;
using TestMethodAttribute = NUnit.Framework.TestAttribute;
using TestInitialize = NUnit.Framework.SetUpAttribute;
using NUnit.Framework;
#else
using TestFixtureAttribute = Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute;
using TestAttribute = Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute;
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif

 

Then I maintain two project files. One is a MSTest project, and only opens in Team System edition. The other is a standard Class Library project – the typical NUnit test project. The later defines NUNIT in its project settings. By using type aliases like this, I can use either NUnit or MSTest attributes in my test fixtures. Of course any tests using NUnits Constraint Model Assertions won’t work in MSTest so I’ll either #if def out those tests, or I won’t include the entire fixture in the MSTest project.

Wunda’s Changing Community

Wendy Friedlander, aka “wunda” has an awesome post over at her site. She says:

The development environment of many institutions is molded to the vision of software provided by people with no insight.

Pushing irrelevant solutions and deadlines.

Traveling a rote technology line.

Where does that leave the rest of us?

We go with the flow.

We find a place that embraces agile.

We work to change our environment.

It takes a great deal of energy to foster change in your environment.

It takes time, effort, trust and many other qualities that make going with the flow look better and better as time goes by.

You are not alone.

Your company software policies were not created by your company.

They are the practices of the community.

A community that conforms to business and technology providers’ desires and not the interest of creating the best solutions.

I love the sentiment, and I love that she is talking about ALT.NET.

It isn’t about the tools. It is about what the tools get you.

“It’s not the tools, it’s the solution.”

This is the phrase that David Laribee used in his ALT.NET post back on April 10th.

I agree, it isn’t the tools. I agree, it is the maintainability of the solution. After “does it work?” Maintainability is the number one concern when I write software.

That said, sometimes “the better way” IS about the tools. I won’t go so far as some others, or as I did previously, BUT (see it is upper case, that means it is a BIG BUT) there are a couple items on the list that deserve to be upper case, or bold, or H1 or Heading 1 or screamed at the top of your lungs while standing on the highest table in the auditorium.

I won’t call it HOT or NOT, I’ll call it Acceptable and Inexcusable.

There are two often used tools which in my opinion have absolutely no place on a well meaning developers project. I’ve given it a bit of thought and I cannot come up with any use case where these tools are the right solution. The answer “they are good enough for our team” is heard by my ears as “we aren’t interested in doing a better job.”

VSS. Yes Visual Source Safe. Lock-Modify-Unlock? What??? At the Visual Studio 2005 launch event I nearly fell out of my chair when the Team System demo showed its Copy-Modify-Merge system. Not because I was impressed. No, I was dumbfounded that the CVS I had been using for 10 years and the SVN I’d been using for a few were so far ahead of what many development groups were using. What wasn’t clear was that VSS2005, while it had a new version, it did NOT change the versioning model.

VSS is inexcusable. If your development organization is using VSS don’t just consider switching. Considering alone would not be enough. DO IT. Switch to ANYTHING! The popular move seems to be subversion, and don’t let the open source fool you. You CAN buy the product and support from Collabnet. It isn’t JUST open source. You get MORE from Collabnet than you do from MS with VSS because if your SVN crashes, they will help you recover and help to find the bug for why it crashed. VSS crashes and is full of known bugs and the answer is to backup your repositories regularly. I’d go on here, but a quick google looking for others citing these VSS flaws shows that Jeff Atwood has already covered this ground. It has been a year so I guess it is time someone else mention it.

I will mention that you might consider changing your SCM paradigm and looking at one of the Distributed tools now available. Bazaar runs great on Windows. Git runs in Cygwin. Mercurial has a windows installer.

MSTest is inexcusable. Yes, the Visual Studio integration is very pretty, but after using NUnit, trying to move to MSTest is painful! It hurts. It hurts bad. When you pile on NUnit’s latest Constraint based testing model, there are MANY things that NUnit can test in only one line of code, that MSTest would leave your writing many many lines of code.

I won’t rant on MSTest nearly as long as I did VSS because I haven’t suffered under its tyranny for nearly as long. Maybe (although I doubt it), with a little more time under MSTest and I’ll be able to bear it, but I stand by my original assertion, it is the wrong choice.

LinqDataSource Select N+1

David Hayden over at CodeBetter has a post on performance and LinqDataSource.

The problem that David Hayden describes so well is known as Select N+1. Those of us using NHibernate have been dealing with this since we started using our favorite ORM. There has been much written on this problem.

It is my belief that this is going to be the biggest problem developers are faced with when using any of the LINQ to database technologies. Entities, DataSets, or SQL, it doesn’t matter which LINQ to database you choose. You will have to understand what is going on under the hood a little. David is using the design time features of the LinqDataSource control and he solves the N+1 problem by limiting his functionality.

Not that it solves any of these problems, but MonoRail just keeps looking better. MR doesn’t have a LinqDataSource, but enabling edits and deletes is no different and treated at a different layer in your typical MonoRail w/ ActiveRecord application. These are different concerns. MonoRail w/ ActiveRecord does the right thing and treats them as separate.

Thank you Castle Project!