Tuesday, 4 April 2006
On code examples
In his blog, Karl Seguin has a thoughtful rant (is that an oxymoron?) about code examples in documentation. He shows some examples from (whew) the Macromedia documentation for .NET.
He finds some examples of particularly egregious bad practices, including:
In a follow-on comment, he notes one point in particular:
- Insecure code.
- Poor error handling.
- Non-meaningful variable names.
- Inefficient and inelegant code.
One concern I had with the Macromedia stuff is I think (and I could totally be wrong) that there'll be a high percentage of copy/pasting. This is material focused towards developers not primarily focused on .NET (or similar languages). They don't know that it's wrong/incomplete. They want to get their flash app working and taking the sample to modify the connection string and select statement might be all the work they'll put into it. Why should they do any more? This does come from Macromedia after all.We've rassled with a lot of these issues. When we write code examples, there is a constant tension, let's call it, between functionality and readability. We very much bear in mind Karl's point about copy-n-paste, one of the behaviors that defines the so-called Mort persona.
Consider database access. We went round and round about how to illustrate this. To accommodate our Morts, we would ideally offer nearly copy-n-paste examples. In that case, it would be easiest to show a) an explicit connection string, and b) explicit credentials. That would minimize the number of changes that the Mort developer would have to make, and it would reduce the cognitive burden required to get the example working.
But these are bad, bad practices. Bad. So we opted for good practice, and upped the work that Mort would have to do to get things running. These days, we show connection strings only when that's somehow essential to the example, and then we show only integrated security. What we mostly do is advise people to keep their connection strings in the config file, which we illustrate. We also somewhat monotonously keep pounding on the message that they should encrypt their connection strings. Doing these non-illustrated tasks is, as they say, left as an exercise for the reader. (We link, of course.) But not doing it -- showing stuff explicitly -- is bad pedagogy.
We also look for places where we illustrate user input, typically with a TextBox control. This one is more subtle. We could always show using Server.HtmlEncode to encode user input. But there are a couple of arguments against doing this always. One, you don't always have to do it. If the example just echoes user input back to the user, there's no point in encoding it. (Yeah, I know -- hold that thought.) Two, if we use it when it's not needed, we clutter the example and perhaps raise questions in the reader's mind about why we're doing it. Three, by default, ASP.NET pages have ValidateRequest turned on, so (at least by default), Mort has a safety net. What we've done is show Server.HtmlEncode in places where you'd obviously use it always. In situations where it doesn't seem mandated, we add a security note (example) that is a heads-up to the reader -- "Hey, this is user input, which you shouldn't trust. Go read about it here."
We've had similar discussions about the use of try-catch and about illustrating exception handling. For example, just how granular is the exception handling in an example? (Catch every exception? Do we unpack possible inner exceptions?) What does the example code in a given catch block do? Although we want to illustrate some sort of non-dumb exception handling, we have to assume that at some point the reader takes over and (we hope) adds some sort of application-appropriate code.
The actual mechanics of creating and vetting code examples have gotten better since the early days of .NET. These days, almost all the code examples are in separate files that are run through FxCop and are compiled (Hey, we can break builds, too!), and are injected into the topics only when we build the docs. Aside from the obvious advantages, this gives us some nice reuse.
I would hardly claim that the code examples are uniformly great. I'm sure people would be delighted to send me all sorts of questionable stuff. (Although pace Kent's comment in Karl's post, it should be pretty darn hard at this point to come up with sightings of
username="sa" password="".) But making code examples better, and adding more of them, was a major goal for Whidbey.
As for elegance, that's a tough one. The ASP.NET writers are all coders, some of them quite experienced. Certainly in newer code samples, the lameness factor should be pretty low. In some cases, there are probably examples where we compromised something for readability, perhaps just to keep the example to a reasonable size. There are some very good examples -- our man Doug wrote up a series of example topics for creating custom providers (membership, roles, etc.) which are not just highly functional, but IMO really excellent example code as well. There are many other such as well.
We're also doing our darndest these days to come up with examples that are non-trivial. A pattern that we try to follow for the reference (API) docs is to have one or more in-depth examples in the class overviews, and then extract relevant excerpts for the individual member topics. Even so, the task can be daunting. Imagine trying to come up with code examples to illustrate the many capabilities of, say, the GridView control.
One final comment about variable names. This was another discussion. For the most part, we try to follow .NET coding conventions. When we show ASP.NET controls, we actually use the convention followed by Visual Studio when it creates controls --
Label2, etc. Theory is this will be consistent with what users will see when working in the product. As I say, that's the theory. One thing I'm reasonably confident about is that you won't find many (any?) variables named
aspnet, whidbey, writing