web-coding 5 ways I write about code

Writing about code isn’t the same as programming.

When you include source code in an article, you have to make certain tradeoffs to make the code more readable for a general audience, including those who may not be very familiar with programming. How you write programs for other programmers is not the same way you would write an article about programming for a nonexpert audience.

I like to write a lot of very technical articles about open source software and programming, including a few books, and I’ve developed a few habits that I like to practice. These are a few of the ways that I write about code in my articles:

1. Walk your way up to it

You can’t just jump into the deep end; you have to walk your readers up to the big idea in smaller steps. For example, if I wrote an article about “a program to print an ASCII table” for a more general audience, I wouldn’t just show the program and write “here it is.”

Instead, I would start by explaining how a “loop” works in the C programming language. I might explain that the for loop has three parts: the initial state, the end condition, and what to do after each pass in the loop. A sample for loop might look like this:

for (count=1; count<=10; count=count + 1) {
  puts("Hello world");
}

That is a simple loop that prints the text “Hello world” ten times. You can see the initial state is count=1, which sets the count variable to 1. The end condition is count<=10, so the loop will continue as long as the count variable has a value less than or equal to 10. At the end of each iteration, the for loop executes count=count + 1, which adds 1 to the count variable.

After that, I might explain a common shortcut: count++ is the same as count=count + 1, and can be shorter to write. I would also show how to write two loops, with one loop embedded in the other. From there, I would add the full code to generate a simple ASCII table, then other code to make the table look nice:

#include <stdio.h>

int main()
{
    int row, col;

    /* header */

    fputs("-- ", stdout);

    for (col = 0; col < 16; col++) {
        printf("%x ", col);
    }

    putchar('\n');                     /* end of line */

    /* body */

    for (row = ' '; row <= 127; row += 16) {
        printf("%x ", row);

        for (col = 0; col < 16; col++) {
            putchar(row + col);
            putchar(' ');
        }

        putchar('\n');                 /* end of line */
    }

    return 0;
}

Finally, if you include a sample program in an article or book chapter, you should also show how to compile and run the program. This helps the reader to follow along, including how to build the program on their own:

$ gcc -Wall -o ascii ascii.c

$ ./ascii
-- 0 1 2 3 4 5 6 7 8 9 a b c d e f
20   ! " # $ % & ' ( ) * + , - . /
30 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
40 @ A B C D E F G H I J K L M N O
50 P Q R S T U V W X Y Z [ \ ] ^ _
60 ` a b c d e f g h i j k l m n o
70 p q r s t u v w x y z { | } ~

2. Use meaningful variable names

In the professional world, developers often use common shorthand notation in their programs for things that other programmers would understand. For example, to write a loop to print a simple list of values, a programmer might use a single letter variable like i or n as the loop variable. That’s very common in professional practice.

But when writing about code, it’s important that your reader can understand everything in the code. The program becomes an extension of the article, which means it must tell its own story. Avoid using single letter variable names; instead, use meaningful variable names.

For example, when writing the ASCII table program, I might have used i and j as my loop variables. For an experienced programmer, the loops are pretty obvious, so the single letter variable names would still be clear. But for a reader who might just be getting started in programming, the single letter variable names could obscure the meaning of the program.

That’s why I used row and col in the program; the row variable tracks the rows in the table, and col tracks the columns in the table. These variable names provide more meaning, and require only a little bit of explanation to help the reader follow along.

3. Write simple code, not flashy code

When I write sample programs in articles, I try to write the most simple code that I can. That doesn’t mean I don’t know how to write programs, only that I want my readers to be able to understand the sample programs on their own.

One way that I keep programs simple is to avoid some cool features of the programming language. For example, in the C programming language, any statement can also be a value. When you reserve memory for an array with the malloc function, malloc returns a pointer to the array, which the program would store in a variable:

int *array;

array = malloc(10 * sizeof(int));

But if the computer doesn’t have enough memory to set aside for that array, the malloc function returns a special value called NULL to indicate a failure. Most programmers would allocate the memory and test for its success by writing a single line of code:

if ((array = malloc(10 * sizeof(int))) == NULL) {
  /* fail */
}

But this can be difficult to read for someone who doesn’t know the programming language very well. Instead, I write the allocation statement and the test as separate lines, which makes the program easier to read:

array = malloc(10 * sizeof(int));

if (array == NULL) {
  /* fail */
}

4. Define variables up front

The “C99” standard for the C programming language added the ability to mix variable declarations within source code. This is a handy feature when writing programs, because programmers can define a variable where they need it. For example, a program to print a list of numbers might include a definition of a count variable within the for statement, which is valid in C99:

#include <stdio.h>

int main()
{
    for (int count = 1; count <= 10; count = count + 1) {
        printf("%d ", count);
    }
    putchar('\n');
    return 0;
}

This makes sense to an experienced programmer, and might be readable to a nonexpert audience. But in my sample programs, I prefer to stick to an earlier C programming standard that requires programs to define variables at the top of the program. I find most general audiences can better understand code when written this way, because it tells a story: these are the variables we’ll use and then this is the sample code.

#include <stdio.h>

int main()
{
    int count;

    for (count = 1; count <= 10; count = count + 1) {
        printf("%d ", count);
    }
    putchar('\n');
    return 0;
}

5. It’s not always optimized

Writing programs that are easy to read do not always use the most efficient routines. But that’s okay for the articles that I write; I’m not teaching a computer science course, I’m writing articles that everyone should be able to understand.

One example is reading and writing data. An efficient method to read data from a file is to read large chunks of the file at a time, such as with the fread function. This data can be stored in an array, which the program can read in smaller parts, such as letter by letter. For example, a sample program to read a large text file might look like this:

#include <stdio.h>

int main()
{
    char buffer[10];
    FILE *input;
    size_t nread;

    int n;

    input = fopen("file.txt", "r");
    if (input == NULL) {
        puts("cannot open file");
        return 1;
    }

    do {
        nread = fread(buffer, sizeof(char), 10, input);
        if (nread > 0) {
            for (n = 0; n < nread; n++) {
                putchar(buffer[n]);
            }
        }
    } while (nread > 0);

    fclose(input);

    return 0;
}

This program doesn’t actually do anything with the data, only prints the results back to the user with the putchar function. In professional practice, a program might use this method to read large chunks of a file, such as to make a copy of the file, or to examine the file for patterns of data. Reading large blocks of data at a time is a very fast way to process data in a program, because the operating system doesn’t have to keep going back to the disk for more data.

While efficient, the fread function is more difficult for nonprogrammers to understand. In theory, you could explain how it works by reading a chunk of data of a specific size (in this case, 10 bits of data, each the size of a single character .. or 10 characters) but that’s a lot of extra text just to explain how to read a file.

If the point of the sample program is to read data from a file and examine that data one character at a time, using fread to read big blocks of data into memory is too much. Instead, I use a slower, simpler approach that reads a single character at a time using the fgetc function:

#include <stdio.h>

int main()
{
    char ch;
    FILE *input;

    input = fopen("file.txt", "r");
    if (input == NULL) {
        puts("cannot open file");
        return 1;
    }

    do {
        ch = fgetc(input);

        if (ch != EOF) {
            putchar(ch);
        }
    } while (ch != EOF);

    fclose(input);

    return 0;
}

The basic function of the program is the same (it reads data from a file and examines it one letter at a time) but the do loop is easier to follow.

This program has another example of writing a program in a way that’s easier for nonprogrammers to understand, even though it is not how a professional programmer would write it. This writes certain statements as multiple lines, where a “real” program would likely condense that code into fewer lines. Another way to write this program might be to condense the fopen and if test into one line, and rewrite the do loop as a more compact while loop:

#include <stdio.h>

int main()
{
    char ch;
    FILE *input;

    if ((input = fopen("file.txt", "r")) == NULL) {
        puts("cannot open file");
        return 1;
    }

    while ((ch = fgetc(input)) != EOF) {
        putchar(ch);
    }

    fclose(input);

    return 0;
}

While this program is shorter to write, it can be more difficult for a general audience to interpret.