Does Bad Writing Reflect Poor Programming Skills?

Writing is a communication skill. And they say that communication skills and the other soft skills are what programmers need today. Effective developers don’t work alone. They work with others in a team. And a team member needs to communicate with the other team members to be effective.

It’s like playing football. No one person can win the game on his own. Each player does his part to determine whether the team moves forward. The center snaps the ball to the quarterback, who hands it off to the half-back, who runs through a hole the offensive line has created in the the other team’s defenses. Or maybe the quarterback passes the ball to a receiver. Occasionally, the receiver is on a different page than the quarterback, and when the ball is thrown, there’s no one there to catch it. The play falls apart. Players either work together, or they lose the game. In football speak, this is called “not executing well.”

The same is true of programming. It was true back in the day, and it’s increasingly true. Today’s technologies are so complicated, no one person can know everything there is to know about any one of them. Do you “know Java”? Yeah, right. Which “Java” is that? Whichever part of Java you know, it’s only a small slice of Java technology. You need to communicate with others in order to leverage their knowledge of the technologies you’re using. And you need to communicate to your teammates your knowledge of the technologies you know and create. And you need to explain to others how to use your code. You need to document your own API’s. Unfortunately, API documentation, both in-house and to outside programmers, stinks.

No wonder. Recruiters and hiring managers say that today’s job is a knowledge-based one. And communication and teamwork skills are more important than ever. And then they go off and play buzzword bingo with a stack of resumes. (And then management commits every teamwork-killing sin in Peopleware, but that’s a different story.) To be fair, recruiters usually know little about software development, so buzzword bingo is the only way they know how to read a resume. And software managers usually know little about communication skills, so they have a good excuse, too. And the cycle perpetuates itself.

The thing is, coding is also a communication skill. Jack Ganssle in his Embedded Muse e-newsletter recently quoted Doug Abbott:

When you write a program, what you’re really doing is communicating to another human what it is you want the computer to do. Programming is about communication, just like any other form of writing. So yes, computer science students should be exposed to good writing. But since there’s so precious little of it in computer science, maybe they just need some basic creative writing classes.

Programmers who don’t know how to write prose probably don’t know how to write code, either.

The AVIFile API

Let’s say you’re programming under Windows. And you’re creating a new AVI file, using the AVIFile API. And the file has two streams in it: a video stream and an audio stream. You want to make sure the audio and video match up, that they are synchronized to each other. If they’re out of sync, you need to time-shift one of the streams to put it in sync with the other.

There is a way to do this. When you create a stream, you provide an AVISTREAMINFO structure, which has the following DWORD field in it:

dwInitialFrames
Audio skew. This member specifies how much to skew the audio data ahead of the video frames in interleaved files. Typically, this is about 0.75 seconds.

That definition is from the Microsoft documentation. Now, here are the problems I have with this.

  1. Since this member defines the “audio skew,” does that mean I can’t use it on the video stream?

  2. Can’t I skew the video data ahead of the audio data?

  3. What are the units? This member is not measured in “seconds,” because it’s an unsigned integer, and seconds is a non-integral value. Presumably the units are “frames.” But video frames or audio frames? The two are way different.

API documentation must be specific, very specific. Remember in college when your lab TA laid out his requirements for lab reports? You had to put it on lab-book paper. And all tables had to be labeled, and each column and row had to be labeled, and you had to label the units for each column. And in all graphs, both the x and y axes had to be labeled and have specific units and had to have labeled tick marks for the values on those axes. Oh how I wish those lessons stuck.

When you write your documentation, don’t just say what the variable or function is for. Tell precisely how to use it. Label each parameter and return value, and give units for each. And describe each important value of the parameter. In particular, define the domain and range of values.

Open Source, and What dwInitialFrames Actually Does

So I used Google to try to find more information on the dwInitialFrames member. And I pulled up a couple interesting, frustrating, and useless tidbits, also presumably written by an engineer who probably knew what he was talking about but who was completely incapable of communicating it to another human being:

  • dwInitialFrames is the “anticipation for audio data to sinchronize [sic] with video frames in interleaved AVI. Typically, 0.75 seconds.”

  • (describing the AVI file header) dwInitialFrames is the “Initial frame for interleaved files. Should set to 0 for Noninterleaved files. When creating interleaved files, players wait for ‘dwInitialFrames’ prior to the initial frame of the AVI sequence in this member.”

I didn’t stick around to read this developer’s code.

I swear I’m not making this up. No, that second quote doesn’t actually parse to real English. So don’t try to figure out what it means… Yet.

While I was searching, I found some code that said:

printf("AVI Initial frame delay: %5.3fn",
       (float)(sh_audio->audio.dwInitialFrames - sh_video->video.dwInitialFrames)
       /default_fps);

This code comes from mplayer, the open-source media player. This is pretty decent code, and it seems to indicate that both audio and video streams can use dwInitialFrames, and that they’re measured in the same units.

And I found the following code in Wine, the open-source Windows-to-Unix compatibilty layer (and don’t try to read this too closely, or your head will explode):

static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This)
{
  LONG lInitialFrames = 0;
  // . . .
  for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
    if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
      lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
  }

  for (i = -lInitialFrames;
       i < (LONG)This->fInfo.dwLength - lInitialFrames;
       i += stepsize) {
    DWORD nFrame = lInitialFrames + i;
    // . . .
    for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
      pStream = This->ppStreams[nStream];

      /* heave we reached start of this stream? */
      if (-(LONG)pStream->sInfo.dwInitialFrames > i)
        continue;

      // . . .
    }
    // . . .
  }
  // . . .
}

I especially love the typo in the logically incorrect comment, “heave we reached start of this stream?” Yeah… “Heave” is right.

But still, thank God for open-source! I’d never get this close to the Microsoft library source code. I think what the above developer meant to say was this (which is logically equivalent to the above):

static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This)
{
  DWORD dwInitialFrames
    = maxInitialFrames(This->ppStreams, This->fInfo.dwStreams);
  // . . .

  for (nFrame = 0; nFrame < This->fInfo.dwLength; nFrame += stepsize) {
    // . . .
    for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
      pStream = This->ppStreams[nStream];

      if (nFrame < dwInitialFrames - pStream->sInfo.dwInitialFrames)
        continue; // We haven't yet reached the start of this stream.

      // . . .
    }
    // . . .
  }
  // . . .
}

This also seems to indicate that all streams can use dwInitialFrames and that they’re all measured in the same units. And given how stepsize is computed (not shown in the code snippet above), it appears the unit of dwInitialFrames is “samples in the first stream.” And since the first stream is traditionally the video stream, and since a video sample contains exactly one video frame…

dwInitialFrames
Stream skew. This member specifies how many video frames worth of data to pre-roll at the beginning of this stream, before time zero. It’s usually used to indicate that audio data is skewed ahead of the video frames, typically by 0.75 seconds. You’d do this by (1) taking the frames per second of video, (2) multiplying by 0.75 seconds, (3) using this product as dwInitialFrames for the audio stream. This means that 0.75 seconds worth of audio will be pre-rolled before multimedia playback starts. Note that under this system, the skew is at best accurate to ±½ video frame. This value is useful only in interleaved files. For non-interleaved files this member should be 0.

But wouldn’t it be nice if the fine developers in Redmond could have managed that? I wonder what the AVIFile code looks like.

And Just So You Know: It’s Not Just Open Source

Here’s a snippet of code from a well funded, closed-source system that has been deployed in production for years. I’ve tweaked it a little, but only to make it more readable. This codebase gave me a migrane and almost made me lose my temper. (Seriously, it did.) I also know who wrote this. He was a senior developer, who had been programming at least as long as I. But as it turns out, in conversation, I never knew what he was talking about without questioning him in detail. Do communication skills reflect programming skills?

float _x = abs(x - deviceInfo->position.x) / scale;
int directionCode;
if (0 < _x && x != deviceInfo->position.x) {
  if (0 > x - deviceInfo->position.x) {
    directionCode = 0x04 /*left*/;
  } else if (0 < x - deviceInfo->position.x) {
    directionCode = 0x02 /*right*/;
  }
}

This is equivalent to the following, more readable code. Except the following actually initializes directionCode in all cases. (The above code has a bug, which the following code fixes.)

static const int DIRECTIONCODE_RIGHT = 0x02;
static const int DIRECTIONCODE_LEFT = 0x04;
static const int DIRECTIONCODE_NONE = 0x00;

int oldX = deviceInfo->position.x;
int directionCode
  = (x > oldX)? DIRECTIONCODE_RIGHT
  : (x < oldX)? DIRECTIONCODE_LEFT
  : DIRECTIONCODE_NONE;

Note that more comments does not mean more readable code. It didn’t in this example. The comments in the snippet above (if you even noticed them) only clutter the code even more. Sometimes fewer comments makes for more readable code. Especially if it forces you to use meaningful symbol names instead.

(By the way, what I remember specifically about the above code is that I did not know what units x was in. I knew it was some angular unit, like degrees or radians. But not one of those. It was documented nowhere, and the knowledge was buried deep within the code. I finally did figure it out. It was hundredths of degrees. But in order to reach that conclusion, I had to ignore the code that never got executed, which had been copied from elsewhere but had not been cleaned up.)

Three Tips for Writing Good Prose

Writing good prose, and good code, is more about style than about substance. Yes, the substance, the message is also important. But if your style is cluttered and unfocused, no one will grok your message. Your substance won’t matter.

Here are three valuable tips writers more experienced than I have taught me about style:

  1. Understand the material before you write. If you understand the material, you should be able to explain it to a four-year-old. If you don’t understand it, you won’t be able to explain it to anyone. You’d think this would go without saying. You’d think that programmers particularly would understand the code they write. But frequently they don’t. If they did, they would use unit tests. Instead, they tweak and hack until the code does what they think it’s supposed to. And they end up with a mess. And then they do it to their comments and other documents, too. This is what causes a lot of comments like “This member is the anticipation for audio data to synchronize with the video frames in an interleaved AVI.” The developer who wrote this probably knew how to tweak the data value to get the result he wanted. But he very possibly did not understand what that value actually represented and how it conceptually fit into the rest of the system.

  2. Write as though your audience is a fourth grader. That is, speak as simply as possible. Use strong, active verbs and concrete nouns. And use short words whenever you can. These two go together. If you do one, you may find yourself automatically doing the other. Never write that “the performance improvements will be implemented according to the plan.” Rather, write, “We will improve how the system performs, as we have planned.” Or better yet, don’t write that sentence at all, if it doesn’t add any information. Each verb should describe a vivid action that the subject of the sentence is doing. And all nouns should refer to actual people, places, and things. Avoid nouns that represent activities, such as “performance” and “improvement.” And the corrolary: Avoid weak verbs, like “implement” and “execute,” which only bridge the gap to other words. Let every word overflow with meaning.

  3. Think about how readers might misinterpret you. Have you ever been in a meeting where two people are arguing over some issue, except that each of them is talking about something different than the other? I’ve seen this happen frequently enough to make a special note of it. Someone is likely to misunderstand me if the words I use have more than one possible meaning. And all words have more than one possible meaning. I need to make it clear which meaning I have in focus in my own mind. I can’t assume that everyone will be thinking along the same lines. Because most people are going to be thinking their own thing. You actually have to exclude meanings that aren’t what you want to say. Be aware of what other meanings your words may have to people who can’t read your mind.

There are lots more great tips in Patricia T. O’Conner’s book Words Fail Me, which I found invaluable in punching up my style. The book’s subtitle is What Everyone Who Writes Should Know about Writing. And that includes programmers.

-TimK

This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

13 Responses to "Does Bad Writing Reflect Poor Programming Skills?"

Leave a reply