Reads Like Prose

Good code reads like prose once it has been broken up into methods, functions, procedures, etc.

Part of the refactoring process that results in WellFactoredCode is to give names to chunks of code. When you have found a chunk of code that can be abstracted and potentially reused, you give it a name by turning it into a method or an object. If these names are well chosen and descriptive, then you begin to approach the point where your code ReadsLikeProse.

This was one of the ideals that the designers of COBOL had back in the 1950's. Of course some shops utterly ignored the possibilities and had their programmers writing code like:

     PERFORM 3010-CALC-PAY-BEGIN THRU 3019-CALC-PAY-END.

3010-CALC-PAY-BEGIN. MULTIPLY TCD-HRS BY EMR-PAYRATE GIVING PCH-SALARY. 3019-CALC-PAY-END. CONTINUE.

But it was possible to write code like this:

     PERFORM CALCULATE-CHECK-AMT.

CALCULATE-CHECK-AMT. MULTIPLY TIMECARD-HRS BY EMPLOYEE-PAYRATE GIVING CHECK-AMT.

Now our modern languages have enough syntactic quirkiness that we can't get quite so close to plain English. But then, managers no longer have any expectation of being able to read code either. But that's no excuse for using excessively ungrammatical features of programming languages. For instance, C allows a programmer to write:

 if (!bufptr){ ... }

rather than

 if (bufptr == 0) { ... }

But the first one does not read like prose and the prose translation if not buffer pointer doesn't make a lot of sense. The second example will compile to the exact same machine code and the prose version if buffer pointer is equal to zero actually makes sense.

--MichaelDillon

FWIW, I find the first phrasing more prosaic. Why? Because I read it "if null bufptr". Zero and equality are algebraic concepts that, to my eye, have very little to do with whether the bufptr is null or not. So I'm forced to infer that. My eye also needs to focus to verify that you've used == and not =, which slows my code scanning. I know yours is the conventional wisdom, and of course YMMV, but for me this is less an example and more a counterexample of ReadsLikeProse, with which otherwise I agree. --PeterMerel

I'm sorry, but I find both of the above examples weak (at least in regards to how much they read like prose). I would be more inclinded to use something like while( Pointer.isNull(bufferPointer) ) { . . . } where isNull is a class method on the Pointer class. Then isNull would be implemented using one of the two above approaches. Thoughts? --HankRoark

[isNull is a horrible name for this action because it has nothing to do with what you want. It stems from the technical domain, not the (local) problem domain. You want to know whether the pointer is valid. isValid() would have been better. You'll find in MFC an AfxAssertValid?() macro for this purpose (and others), for instance.

Furthermore, if you consider all objects capable of being evaluated in a Boolean context which returns their validity, then !bufptr makes a lot more sense than bufptr == 0. In C++, I usually have an "operator bool() const { return IsValid(); }" if there is a concept of validity. The iostream libraries use the same mechanism (though they may return a void * on older compilers). (Not just on older compilers -- on any standards conforming C++ implementation, as void* is required. Strangely it turns out that in some ways void* is a better-behaved boolean type in C++ than bool is (and there are better still, but they weren't understood in the mid-to-late 90s. -- JamesDennett.)

Moreover, since I'd rather test rvalue == lvalue to prevent lvalue = rvalue errors, I have to remember to enter this as 0 == bufptr. What a pain.

Finally, bufptr is one of those really BadVariableNames. In my opinion, aim for SelfDocumentingCode if you want readability. -- SunirShah]

Just rename bufptr to buffer.

 if(!buffer) { ... }
Reads fine: "If no buffer..." -- Anonymous


In the past I have used similar constructs using C macros to emulate what 88 levels do in COBOL. For instance:

 77 BUFPTR PIC 9999.
    88 NULL-BUFPTR VALUE 0.

IF NULL-BUFPTR THEN

becomes

 #define isNull(x) (x == 0)

if (isNull(bufptr)) { ... }

However, to more directly address Peter's comments, I admit that my example was flawed since modern C compilers have a defined macro NULL that could be used like so:

 if (bufptr == NULL) { ... }

--MichaelDillon


As for me, I would find a formula such as

 checkAmount := timecardHours * employeePayRate
far easier to read than

 MULTIPLY TIMECARD-HRS BY EMPLOYEE-PAYRATE GIVING CHECK-AMT.
More generally, I hold that in using a programming language, it's important to learn to read it, and write it, idiomatically, as the "native" speakers use it. People often complain about C-isms such as

 while (*t++ = *s++);
even though to the practiced reader these are quite clear. While the idea that code should read like prose is an interesting one, it might be best taken other than literally. Code should be clear, elegant, consistent with expert usage of the language in question. It should lead the eye and the mind to understanding. But read like prose? Perhaps not. -- RonJeffries

I think the line of COBOL is closer to ReadsLikeProse because it reads left to right, unlike the C version. Any arguments? -- ShaeErisson

The C version also reads left-to-right; it's just not executed left-to-right. Furthermore, the C ordering seems clearer to me: the goal is stated first, followed by how to get there. With the COBOL version, I'm most of the way through the sentence before I know what it's driving at.

I'm mostly in agreement with Ron. Code should read clearly, left-to-right, top-to-bottom, and focus on intent. But just as good prose is targeted at skilled readers of the language it's written in, good code should aim at programmers who know the language. -- GlennVanderburg

maybe ReadsLikeGerman is more apt. -- ShaeErisson

Or ClassicalGreek (It's been suggested that FunctionalProgrammingLanguages read like ClassicalGreek. [Though this may just be a case of SmugWeenies? flocking together].)


Some code should ReadLikeMath, if mathematics is the natural language of the problem domain. -- DaveHarris


The other issue here is whether you subvocalize when you read or just "see it". If you have that cognitive style, ReadsLikeProse is great for you.


This raises an interesting (?) topic: if there are different ways of perceiving program text, how should we react to this in our coding standards [if at all]?

<advocacy> I use the PythonLanguage. Since there is almost always one best way to do something with it, its readability is very high. (if you stay away from its functional bits) </advocacy> -- ShaeErisson


This is one of the reasons I like the PerlLanguage. Syntax such as

  $substitutions{shortName} = MakeShortName?( $name ) if $useShortName;
  $substitutions{urlName  } = MakeURLName(   $name )   if $useURLName;
  ...

or

  my $sum = 0;
  $sum += shift @input while @input;

seems to read very easily.

Some other SyntacticSugar I like:

  print $_ foreach @array;

die "Oops" unless $condition;


ReadsLikeProse is about BelievingAbstractions.


I'm not certain that reading like prose is a benefit.

When algebra was first invented, people didn't use variables, operators or other notation. They just wrote things out in natural language. Here is an example from Al-Khwarizmi's famous book:

If the instance be, 'ten and thing to be multiplied by thing less ten,' then this is the same as 'if it were said thing and ten by thing less ten. You say, therefore, thing multiplied by thing is a square positive; and ten by thing is ten things positive; and minus ten by thing is ten things negative. You now remove the positive by the negative, then there only remains a square. Minus ten multiplied by ten is a hundred, to be subtracted from the square. This, therefore, altogether, is a square less a hundred dirhems.

Compare this to (x+10)*(x-10) = x*x + 10*x - 10*x - 10*10 = x*x - 100

Now imagine trying to do even simple calculus like this. If there's a lot of essential complexity, I claim that natural language becomes much harder to understand than specialized notation, which is why notation exists in the first place. A good notation is understood on its own terms, not in terms of translation to some natural language statement, for instance, algebraic manipulations are done according to certain rules, without having to re-justify them each time, because of the underlying soundness of algebra.


See HungarianNotation


EditText of this page (last edited July 3, 2008) or FindPage with title or text search