I <3 Steve McConnell*
Coding Horror
programming and human factors
by Jeff Atwood


23 posts from November 2005

November 30, 2005

Whatever Happened to the META Tag?

When was the last time you saw a HTML header like this?

<head>
<title>GUID World</title>
<meta name="description" 
  content="Everything you wanted to know about GUIDs but were afraid to ask">
<meta name="keywords" 
  content="GUID, UUID, globally unique identifiers, 128-bit">
</head>

The web is a metadata-free zone. It's widely known that Google completely ignores metadata in its indexes. The <meta> tag has fallen so far out of favor that it drags the whole concept of metadata down with it. And perhaps rightfully so. Cory Doctorow viciously deconstructs metadata in Metacrap: Putting the torch to seven straw-men of the meta-utopia:

There are at least seven insurmountable obstacles between the world as we know it and meta-utopia. I'll enumerate them below:.

1. People lie

Metadata exists in a competitive world. Suppliers compete to sell their goods, cranks compete to convey their crackpot theories (mea culpa), artists compete for audience. Attention-spans and wallets may not be zero-sum, but they're damned close. That's why:

  • A search for any commonly referenced term at a search-engine like Altavista will often turn up at least one porn link in the first ten results.
  • Your mailbox is full of spam with subject lines like "Re: The information you requested."
  • Publisher's Clearing House sends out advertisements that holler "You may already be a winner!"
  • Press-releases have gargantuan lists of empty buzzwords attached to them.

Meta-utopia is a world of reliable metadata. When poisoning the well confers benefits to the poisoners, the meta-waters get awfully toxic in short order.

The other six reasons are equally caustic, and all have a common theme: relying on users to create accurate metadata means you're betting on an optimistic view of human behavior. And we all know how well that works out.

Which brings me to the complete abandonment of the <meta> tag. Isn't it ironic that groups still advocate manually adding metadata to web pages? Who, exactly, is adding The Dublin Core Metadata Element Set to the <head> section of their web pages? Nobody, that's who.

Manual metadata may be suspect, but automated generation of metadata is practically the holy grail. Google's entire 450 zillion dollar market cap is predicated on one tiny, automatically generated piece of metadata on every web page they index: PageRank. Popularity rules the web. It's high school all over again: either you're popular and people link to you, or.. well, good luck on that whole prom thing.

But popularity has some limitations. For one thing, PageRank doesn't work on an intranet. Office documents are rarely HTML, rarely linked to each other, and you probably don't have a large enough sample set to do any fancy statistical analysis, either. That's why the Google Search Appliance not only actively indexes metadata in the <meta> tag, it requires metadata to return relevant results. It's right in the manual. Just try doing that with the capital-g Google.

Perhaps that's why Tim Bray steadfastly maintains that some form of metadata is necessary to improve search results.

One of the Web's distinguishing features is that there's a big gaping hole where the metadata ought to be. The Web has resources, identified by URI, and you can ask for "representations," which come with some metadata, but the metadata is about the representation, not the resource. Given a URI, the Web has no built-in way to ask questions about it, for example "What is this about?" or "When does it expire?" or "Is this suitable for children?" or "Is this good?"

I'm not an advocate of the utopian semantic web, mind you, but I sure would like something that can tell the difference between a Jaguar and a Jaguar instead of telling me which one is more popular.

Posted by Jeff Atwood    8 Comments

November 29, 2005

In praise of Beyond Compare

It's a shame that Beyond Compare isn't listed in more "favorite tool" lists. This amazing little folder and file differencing tool has earned its spot in my core toolset a dozen times over. Here's a screenshot of it in action:

Screenshot of Beyond Compare in action

I've mentioned Beyond Compare before, but even a year later, few developers seem to know about it. If you haven't tried it yet, what are you waiting for? Are you writing your own diff program in c#? Give it a 30-day trial spin, and check out the viewer plugins, too.

Here are a few beginner tips when using Beyond Compare:

  • Make use of the ultra-handy right-click explorer file and folder context menus to compare stuff.
  • The default comparison is file size plus date and time, which is super speedy but not always accurate. Use the little scales icon (or the Session | Comparison Control menu) to switch the comparison type to Size and CRC.
  • When viewing a file comparison, use the referee icon (or the Tools | Edit Current Rules menu) to tweak the file comparison rules. I usually turn off all the options under "Unimportant Text" such as leading whitespace, case, line endings, etcetera.

Posted by Jeff Atwood    51 Comments

November 28, 2005

C#, VB.NET, and echoing strings in the VS.NET Immediate Window

I've become rather agnostic on the whole topic of C# versus VB.NET, but there are still those annoying little differences that sneak up behind you and rabbit-punch you in the kidneys. Like, say, using the VS.NET 2003 command window in immediate mode to print a string:

Printing text in the Immediate window in a C# project

Printing text in the Immediate window in a VB.NET project

Usually VB.NET is guilty of handling things in odd and unexpected ways, but this time it's C#. I expect newlines to appear in the immediate window as, oh, I don't know.. NEW LINES? It's a bit difficult to read text filled with a bunch of \r\n notation instead of human readable whitespace. What's worse is that even the variable tooltips in C# behave this way!

How can I work around this annoying "feature" of the C# IDE? I thought about creating a macro to use ?System.Diagnostics.Debug.WriteLine(s), which behaves appropriately, but the Diagnostics class isn't always in scope at a breakpoint.

Posted by Jeff Atwood    11 Comments

November 27, 2005

Dysfunctional Specifications

The guys at 37signals think functional specs are worthless:

Don't write a functional specifications document. Why? Well, there's nothing functional about a functional specifications document.

Functional specifications documents lead to an illusion of agreement. A bunch of people agreeing on paragraphs of text is not real agreement. Everyone is reading the same thing, but they're often thinking something different. This inevitably comes out in the future when it's too late. "Wait, that's not what I had in mind..." "Huh? That's not how we described it." "Yes it was and we all agreed on it -- you even signed off on it." You know the drill.

Functional specifications document are "yes documents." They're political. They're all about getting to "yes" and we think the goal up front should be getting to "no." Functional specs lead to scope creep from the very start. There's very little cost in saying "yeah, ok, let's add that" to a Word document.

Functional specs are often appeasement documents. They're about making people feel involved. But, there's very little reality attached to anything you do when the builders aren't building, the designers aren't designing, and the people aren't using. We think reality builds better products than appeasement.

Functional specs are about making decisions before you have enough information to decide. They are about predicting the future and we all know how accurate that is.

In lieu of a functional spec, the guys at 37 Signals recommend writing a simple one page story of what the app should do. This probably isn't a functional spec in the traditional sense; it's more like a vision statement. And you should have a vision statement, because half the developers on your team probably can't tell you what they're building anyway.

Linus Torvalds evidently hates functional specifications, too:

A "spec" is close to useless. I have never seen a spec that was both big enough to be useful and accurate. And I have seen lots of total crap work that was based on specs. It's the single worst way to write software, because it by definition means that the software was written to match theory, not reality. There are two major reasons to avoid specs:

  1. They're dangerously wrong. Reality is different, and anybody who thinks specs matter over reality should get out of kernel programming NOW. When reality and specs clash, the spec has zero meaning. Zilch. Nada. None. It's like real science: if you have a theory that doesn't match experiments, it doesn't matter how much you like that theory. It's wrong. You can use it as an approximation, but you MUST keep in mind that it's an approximation.
  2. Specs have an inevitable tendency to introduce abstraction levels and wording and documentation policies that make sense for a written spec. Trying to implement actual code off the spec leads to the code looking and working like CRAP. The classic example of this is the OSI network model protocols. Classic spec-design, which had absolutely zero relevance for the real world. We still talk about the seven layers model, because it's a convenient model for discussion, but that has absolutely zero to do with any real-life software engineering. In other words, it's a way to talk about things, not to implement them. And that's important. Specs are a basis for talking about things. But they are not a basis for implementing software.

So please don't bother talking about specs. Real standards grow up despite specs, not thanks to them.

In defense of functional specs, there's a rebuttal of Linus' position at Canned Platypus. And Joel has a persuasive three-part essay championing functional specifications that's chock full of great advice; I highly recommend reading all three parts. Unfortunately, you know you're in trouble when Joel tells you how hard it is to write functional specifications that people actually read:

The biggest complaint you'll hear from teams that do write specs is that "nobody reads them." When nobody reads specs, the people who write them tend to get a little bit cynical. It's like the old Dilbert cartoon in which engineers use stacks of 4-inch thick specs to build extensions to their cubicles. At your typical big, bureaucratic company, everybody spends months and months writing boring specs. Once the spec is done, it goes up on the shelf, never to be taken down again, and the product is implemented from scratch without any regard to what the spec said, because nobody read the spec, because it was so dang mind-numbing. The very process of writing the spec might have been a good exercise, because it forced everyone, at least, to think over the issues. But the fact that the spec was shelved (unread and unloved) when it was completed makes people feel like it was all a bunch of work for naught.

Also, if your spec never gets read, you get a lot of arguments when the finished product is delivered. Somebody (management, marketing, or a customer) says: "wait a minute! You promised me that there would be a Clam Steamer! Where's the clam steamer?" And the programmers say, "no, actually, if you look on the spec on chapter 3, subchapter 4, paragraph 2.3.0.1, you'll see it says quite explicitly 'no clam steamer.'" But that doesn't satisfy the customer, who is always right, so the grumpy programmers have to go retrofit a clam steamer into the thing (making them even more cynical about specs). Or a manager says, "hey, all the wording on this dialog is too verbose, and there should be an advertisement at the top of every dialog box." And the programmers say, in frustration, "but you approved the spec which precisely listed the layout and contents of every dialog box!" But of course, the manager hadn't actually read the spec, because when he tried, his brain started seeping out through his eye sockets, and anyway, it was interfering with his Tuesday golf game.

So. Specs are good, but not if nobody reads them. As a spec-writer, you have to trick people into reading your stuff, and you should also probably make an effort not to cause any already-too-small brains to leak out through eye-sockets.

If it takes a writer as good as Joel to trick people into reading functional specs, the rest of us are totally screwed. I guess that's hard to see when you're Joel, but it's been obvious to me on every project I've ever worked on.

Having spent the better part of the last two months writing functional specifications, I'm of two minds on them:

  1. Yes, you absolutely should plan what you're going to do before you do it.
  2. No, you shouldn't try to plan everything you do in excruciating detail, because you'll forget a million things, then on top of that you'll encounter a bunch of problems you hadn't even considered, so you'll end up rewriting it all anyway.

You have to strike some kind of balance between a one-page vision statement and an obsessively detailed five volume masterwork of a functional spec. I can't tell you what level of specification is appropriate for what you're doing, but here's what has worked for me:

  • When in doubt, err on the side of that one-page vision statement. At least people will read that. Maybe.
  • Plan a little, then code a little. Repeat. Planning and implementation should overlap; they're symbiotic processes. Too much planning is just as wrongheaded as no planning.
  • Your spec should be a dynamic, living document. It should change and grow along with your code. Have you considered making your spec a Wiki?

Posted by Jeff Atwood    23 Comments

November 26, 2005

The Many Faces of (Windows) Death

As I recall, the Blue Screen of Death was introduced with Windows NT 3.1 circa 1993:

A blue screen of death occurs when the kernel, or a driver running in kernel mode, encounters an error from which it cannot recover. This is usually caused by a [hardware] driver that throws an unhandled exception or performs an illegal operation. The only action the user can take in this situation is to restart the computer, which results in possible data loss due to Windows not properly shutting down.

Blue screens are known as "Stop errors" in the Windows NT/2000/XP documentation, and are also sometimes referred to as "bugchecks".

The "Stop" message contains the error code and its symbolic name (e.g. 0x0000001E, KMODE_EXCEPTION_NOT_HANDLED) along with four error-dependent values in parentheses. Depending on the error code, it may display the address where the problem occurred, along with the driver which is loaded at that address. Under Windows NT and 2000, the second and third sections of the screen contain information on all loaded drivers and a stack dump, respectively. The driver information is in three columns; the first lists the base address of the driver, the second lists the driver's creation date (as a Unix timestamp), and the third lists the name of the driver.

The BSoD is analogous to a Kernel Panic in the UNIX world, and it became a standard fixture in all subsequent versions of Windows. Here's a chronological pictorial, starting with Win9x and going up to WinXP:

Windows 9x BSOD Windows NT 3.1 BSOD
Windows NT 3.5 BSOD Windows 2000 BSOD
Windows XP BSOD

Microsoft catches a lot of flak for the Blue Screen of Death, but as frequently noted throughout the BSoD documentation, most BSoDs are due to faulty third-party hardware drivers.

If you're a glutton for punishment, you can attempt to decipher all that technical jibba-jabba on the BSoD or perform BSoD troubleshooting. If you're like most of us, you just cross your fingers and reboot. You may not see the BSoD much in Windows XP or Windows Server 2003 because the latest versions of Windows are configured to reboot automatically after a BSoD is encountered. Why wasn't it always this way -- after all, what the heck else are you going to do after getting a BSoD? Have a party?

After rebooting, you could celebrate by buying a BSoD T-Shirt:

The Blue Screen of Death T-Shirt

And for a few laughs, you can install the BSoD screensaver on a coworker's computer when they're not around. This thing is highly authentic-- it even simulates a reboot-- and quite scary when you're not expecting it. In other words, it's hilarious.

The bluescreen is so well known now that it has become a "(color) Screen Of Death" meme. Sometimes it's used correctly, in the case of the Xbox 360 Black Screen of Death -- which appears to be related to overheating*. Sometimes it's expanded incorrectly to include things that have little to do with deep Kernel-level failures, such as the Yellow Screen of Death in ASP.NET:

The so-called Yellow Screen of Death

It's only partially yellow, and only partially a screen o' death. If you see it, your web app may be unavailable, but your server certainly isn't bluescreening.

* See the massive internal Xbox 360 heatsink to see why I think this is the case. It's a common problem with consoles when they're placed on carpet or in restricted areas with limited airflow. My Nintendo 64 used to overheat!

Posted by Jeff Atwood    21 Comments

November 21, 2005

Avoiding Undocumentation

Have you ever noticed that much of the online MSDN .NET framework help is.. not helpful? Take the the MSDN help for the IBindingList.AddIndex method, for example:

MSDN documentation for IBindingList.AddIndex method

Scott Swigart calls this undocumentation, and elaborates further in his blog post:

This is an example where quacking like a duck doesn't make you a duck. This looks like documentation. It shows up in the help alongside documentation, it's indexed like documentation, but it's not documentation. It doesn't actually tell anyone anything they didn't already know.

Large swaths of the Framework are undocumented in exactly this way, and many v1.0 SDKs are, well, very undocumented.

Honestly, my problem isn't that lots of stuff is undocumented. It's that Microsoft spent time writing this undocumentation, proof-reading this undocumentation, and putting this undocumentation through the same process as the real documentation. I don't know how much time was spent undocumenting things, but I'm guessing that if you add it all up, it's a lot.

I guess on the documentation teams, there must be some law that no class, property, method, or event will show up in the help with a big, bold, "Undocumented".

Can we stop pretending? Can you just mark everything as Undocumented until you get around to writing real documentation for it? Maybe even include a "Click here to vote to have this documented." For a simple test, if it doesn't include a code example, it's not documented. Just mark it as such and move on.

What really scares me is that tools like GhostDoc produce exactly this kind of useless undocumentation. Now, I understand that GhostDoc is just a tool intended to assist developers in producing real documentation. And like all tools, it can be used properly or abused. But whatever you do, please don't knowingly produce undocumentation for your applications. Have some respect for your users and your fellow developers. Either take the time to write helpful documentation, or have the guts to acknowledge that there simply is no documentation.

I encounter undocumentation all the time when I'm rooting around for help on the .NET framework. For example, take a look at the MSDN help for the PassportIdentity.SignOut method*. This particular example of undocumentation is even more egregious, because it actually includes code samples! Utterly useless, one-line code samples. In each language.

' This example demonstrates how to sign a user out of Passport.
' local GIF file that the user is redirected to.
Dim signOutGifFile As String = "signout.gif"
' Signs the user out of their Passport Profile and displays the SignOut.gif file.
System.Web.Security.PassportIdentity.SignOut(signOutGifFile)

// This example demonstrates how to sign a user out of Passport.
// local GIF file that the user is redirected to.
string signOutGifFile = "signout.gif";
// Signs the user out of their Passport Profile and displays the SignOut.gif file.
System.Web.Security.PassportIdentity.SignOut(signOutGifFile);

But hey, at least the code samples are valid. As a Vertigo developer pointed out a few months ago, the code samples on the MSDN help page for IComparable.CompareTo aren't even correct:

public class Temperature : IComparable
{
    public int CompareTo(object obj) {
        if(obj is Temperature)
        {
            Temperature temp = (Temperature) obj;
            return m_value.CompareTo(temp.m_value);
        }        
        throw new ArgumentException(
            "object is not a Temperature");    
    }

    protected int m_value;

    public int Value
    {
        get { return m_value; }
        set { m_value = value; }
    }

    public int Celsius
    {
        get { return (m_value-32) / 2; }
        set { m_value = value * 2 + 32; }
    }

There are any number of websites documenting how to convert Fahrenheit to Celsius and vice-versa:

  1. Take the temperature in Fahrenheit and subtract 32.
  2. Divide by 1.8.
  3. The result is degrees Celsius.

Oddly enough, the C++ sample on the same page is correct. I guess C++ developers really are smarter.

Grousing about all this undocumentation is funny, but it doesn't magically produce useful documentation. Here's something that might, though: why not make the .NET framework documentation a Wiki?

* Yes, we work on a few apps that use Microsoft Passport. God help us. God help us all.

Posted by Jeff Atwood    19 Comments

November 20, 2005

Conversations with Erich Gamma

Artima has another great interview series, this time with Erich Gamma. You know, Erich Gamma: Gang of Four, JUnit, Eclipse. As you might expect from such a notable developer, it's full of great advice. Like this section on avoiding frameworkitis:

Frameworkitis is the disease that a framework wants to do too much for you or it does it in a way that you don't want but you can't change it. It's fun to get all this functionality for free, but it hurts when the free functionality gets in the way. But you are now tied into the framework. To get the desired behavior you start to fight against the framework. And at this point you often start to lose, because it's difficult to bend the framework in a direction it didn't anticipate. Toolkits do not attempt to take control for you and they therefore do not suffer from frameworkitis.

If we do frameworks, we try to make them small frameworks. We prefer many small frameworks over one heavyweight framework. The bigger the framework becomes, the greater the chances that it will want to do too much, the bigger the learning curves become, and the more difficult it becomes to maintain it. If you really want to take the risk of doing frameworks, you want to have small and focused frameworks that you can also probably make optional. If you really want to, you can use the framework, but you can also use the toolkit. That's a good position that avoids this frameworkitis problem, where you get really frustrated because you have to use the framework. Ideally I'd like to have a toolbox of smaller frameworks where I can pick and choose, so that I can pay the framework costs as I go.

Highly recommended.

If you enjoyed that, you'll also enjoy a similar series of interviews with the Pragmatic Programmers. The entire series is linked at the bottom of this post.

Posted by Jeff Atwood    2 Comments

November 18, 2005

Comparing Font Legibility

If you're not reading the Wichita State Software Usability Research Laboratory newsletter regularly, you should be. It's an amazing source of usability experiments with actual data, hypotheses, citations, statistics, and all that other stuff that puts the science back into computer science.

A 2001 SURL experiment compared the legibility of twelve common web fonts on a then-typical 17" monitor running at a resolution of 1024x768. And before you read any further, you should first view the font samples.

Examining the mean reading time for each font type irrespective of their accuracy, we found significant differences. The reading time difference between [Corsiva and Tahoma] was 40 seconds for approximately two pages of text. This finding of a relatively small difference in reading speed between these fonts has been consistent with our previous studies.

font legibility results graph

I'm surprised that they consider a difference of 40 seconds -- that's 13 percent of the total reading time of ~310 seconds -- "relatively small". A 13 percent difference in reading time between the pseudo-calligraphic, completely inappropriate Corsiva and Tahoma isn't terribly surprising.

But here's what is: Courier -- the only monospace font in the experiment -- was the second worst font for reading time. Courier barely performed any better than the abhorrent Corsiva! Any connection between reading paragraphs in a browser and reading code in an IDE is unclear, but it certainly doesn't bode well for the use of monospace fonts in general.

In the end, the reading speed differences between the various fonts may not be large enough to be significant. At least not for the type of reading most people do on the computer:

.. the different reading speeds associated with various font types may not be of any real consequence for short online passages -- as long as the fonts are within the conventional font size and type range.

Before worrying about fonts, be sure you aren't presenting giant blocks of text to the user in the first place. Well-designed web pages avoid using large blocks of text, and so should your code.

Posted by Jeff Atwood    16 Comments

November 17, 2005

Learning from TEH INTARWEB

I try to avoid posting entries from the Mindless Link Propagation Department, but Adam Bosworth's article Learning from The Web is excellent and it deserves a careful read:

The Web taught us several unintuitive lessons:

  1. Simple, relaxed, sloppily extensible text formats and protocols often work better than complex and efficient binary ones
  2. It is worth making things simple enough that one can harness Moore's law in parallel
  3. It is acceptable to be stale much of the time
  4. The wisdom of crowds works amazingly well
  5. People understand a graph composed of tree-like documents (HTML) related by links (URLs).
  6. Pay attention to physics
  7. Be as loosely coupled as possible.
  8. KISS. Keep it (the design) simple and stupid

In addition to all the architectural stuff Adam outlines, we can't overlook the critical contribution of web browser user interface. The browser popularized the web by making it dead simple to use:

  • The entire interface is driven by a single mouse click. Nary a double-click in sight! And none of the interface is obscured behind things like drop-down menus; it's all immediately visible and immediately clickable.
  • The back button. Easily the most profound innovation in user interface in the last ten years. Don't like where you are? Go back to where you were. It's totally natural.
  • Each webpage focuses on a single task. Since websites are slow to load and can't display much at once, designers were forced to stick to one task per page, along with a palette of relevant tasks for that activity. A refreshing change of pace from your typical monolithic application with its monster twenty-item cascading main menu.

These "webby" UI characteristics are now collectively known as Inductive User Interface, and figure promimently in many newer client applications and operating systems.

Adam also defines some of the shortcomings of XML, and in the process, proposes RSS as an all-out replacement for XML:

Some of us learned these lessons seven or eight years ago and applied them to distributed computing. We had the explicit goal of using XML over HTTP to exchange information between applications via messages to build a loosely coupled, robust, Web-friendly model for distributed computing. In my humble opinion, however, we ignored or forgot lessons 3, 4, and 5.

RSS 2.0 has reached the tipping point where it is universally understood. [..] It may well be that a lot of useful information is going to flow over the Web as either RSS 2.0 or Atom (or both, depending on the type the requester asks for). It addresses many of the serious limitations or outages in XML today.

The last section on databases reads like a logical continuation of the "RSS defeats XML" thought, and more specifically, an argument in favor of Google Base. Did I mention that Adam works for Google?

Posted by Jeff Atwood    5 Comments

November 16, 2005

Software Apprenticeship

In Software Training Sucks: Why We Need to Roll it Back 1,000 Years, Rob Walling makes a compelling argument for abandoning traditional training classes in favor of apprenticeships:

[Why not] use the time-tested approach of trades that have been doing it for years? Let's take an electrical apprenticeship as an example: in the United States today, the International Brotherhood of Electrical Workers (I.B.E.W.) trains thousands of electricians every year. They learn through two distinct experiences:

  1. Attending night school during the week to learn the theory of electricity.
  2. Working days on a construction site where they're able to gain experience applying the theory to the hands-on construction of a building

His first day on the job an apprentice is paired up with a journeyman (an experienced electrician), who shows him the ropes. The journeyman typically talks the apprentice through a task, demonstrates the task, has the apprentice perform the task, then gives feedback. Listen, watch, do, review.

Ye Olde Apprenticeship

With software it looks like this: the mentor evaluates the task at hand, be it writing data access code or building a web-based user interface, and holds a white-board discussion with the apprentice (listen). Next, the mentor might write sample code demonstrating a particularly difficult or confusing concept (watch). At this point the mentor sends the mentee off to gain their own experience writing code (do). And finally, the mentor should review the code, providing positive and negative feedback and suggesting improvements (review). Listen, watch, do, review.

[..] the key to any type of apprenticeship is the "do" step. Most software training gives you the listen and watch, but the "do and review" is what inspires growth and advances skills. The beauty of apprenticeship is that it tackles theory and experience in one fell swoop. And it's easier than you think.

Instead of a loose confederation of tribes, maybe we should be cultivating apprentice/journeyman/master relationships in software development.

The mixture of theory by night and real world coding by day is particularly compelling. Maybe this is why I've seen so many talented interns turn into amazing developers -- they're working on real business code while getting the computer science courseware theory, too.

Being a good mentor isn't easy, though. I have difficulty mentoring developers who are too far apart from me in skill level. I'm too impatient. If you're putting football players together on a field to scrimmage, don't mix professional players with high school players. The skill disparity is too great for them to actually play football together. And how can they learn without playing the game? Now, if you throw some college football players in the mix, it's on!

Posted by Jeff Atwood    16 Comments
Read older entries »
Content (c) 2009 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved.