« I Suppose I should Be Flattered | Main | I've Finally Been Subversioned »

Writing Readable Code

Listen to this articleListen to this article

Sometime ago I wrote about my experiences pairing with James and the effect that listening to your code can have on the naming of variables, methods, classes, etc. Then last night, I had a very interesting discussion with Owen Rogers about, among other things, the effect that TDD has on your code.

One of the observations was that, in order to write a test (and by inference mainline code) that is understandable, you need to name your methods very carefully. The specific example that Owen gave was a factory method for creating an XmlWriter. The usually accepted convention is to name the method createXXX or newXXX as in:

public class XmlWriterFactory {
    public XmlWriter createWriter() {
        ...
    }
}

Which would then be used something like:

order.write(factory.createWriter());

The problem with this is that it doesn't really flow particularly nicely. To illustrate, let's re-write this last line of code into English:

order (dot) write, factory (dot) create writer

Reading the this aloud seems a little unpleasant and requires you to think a little too much about what is going on, even with such a simple line of code.

The suggested improvement was to buck the "trend" and rename some of the methods so that the code would look something like:

order.writeTo(factory.xmlWriter());

This can then be converted plain English that reads something like:

order (dot) write to , factory (dot) xml writer

Much nicer! Written this way, it's much easier to get a feel for what is going on and for that matter to ignore the occasional syntactic noise such as "dot" (and for that matter "factory"). Interestingly however the necessary changes to the factory class make the class itself seem a litte odd:

public class XmlWriterFactory {
    public XmlWriter xmlWriter() {
        ...
    }
}

If you looked at the modified factory class in isolation it might not be obvious what the method xmlWriter was doing. Because there is no verb attached to the name, we have lost some of the meaning of the method when viewed on its own. And, to me, this is one major difference between a traditional design-your-classes-up-front approach, versus a more design-your-classes-as-you-need-stuff strategy.

It seems there is a trade-off between understanding a class in isolation versus understand its use in context and the nice thing about simple, readable tests, is that they give you that context as well as all the other benefits associated with good quality unit tests.

Of course this doesn't help much if you're building an API that needs to be used by someone else. Chances are you probably don't distribute your unit tests and even if you did, people still feel less comfortable reading test code than mainline code or JavaDoc. Having said this, it has always been my experience that no matter how good the documentation is it's usually almost impossible to work-out how a library should be used (especially one that is composed of many small objects) unless you have concrete examples - code presented in context.

There are obviously many other factors that affect the readability of code and naming is but one however I do find all this particularly interesting as it challenges many of the assumptions, habits and conventions that I, and plenty of others, have formed over the years.

Even more interesting to me is that some approaches favour understanding by novices while others seem to benefit more experienced developers - what seems like the simplest thing™ to one person is completely unobvious to another.

Comments

Simon, I wonder if writeTo() feels more natural to you than write() because you’re a Australian. We say we will “write to” someone (intransitive), while Americans “write” someone (transitive).

Just a thought.

—Michael

Indeed, you could be quite right. That so much of software development is, as this example demonstrates, so subjective just furthers the argument that our profession is (still) rather less than scientific in nature.

Hi Simon,
I also use to invest time finding a good name for my classes, methods and objects. I've found that most people don't pay enough attention to this point. But I agree with you that readable code is far more easy to understand. The time invested in finding good names saves much more time in comments and maintenance.

About your example I has coded so much C++ as Java and the xmlWriter() is the usual form for a C++ getter so it looks meaningful for me considering it is in a Factory class.
From my point of view Java (Bean) naming conventions were designed for tools (IDE's, J2EE...) and not for humans, and they are not so readable as they should be, but we have been forced to use them, so now we keep using them , even when it isn't required.

I disagree with your library unusability comment. I've found some which don't need examples at all and many that only need little. From my point of view it's a matter of how good the artifacts names are, if the documentation is synchronized with the code, and how many undocumented features you need to use it. I think that most examples breed our lazyness, since many times we don't have the time and/or the willingness to read the documentation, so we claim for an example to cut & paste. It also has some smell of a marketing issue since as a library designer I would invest time in writting examples and recipes, so much more people will use it.

Regards.

Rodolfo Martin

Locally, our coding convention is to use "new" (rather than "create") as a factory method prefix. Under which your example would become "order.writeTo(factory.newXmlWriter())".

Hadn't fully realised until reading your blog entry, but I think one reason I like said convention is that the prefixed method name does still clue me in at the point of declaration that this is a factory method, but the client use "factory.newXmlWriter()" reads better as an English compound noun than "factory.createXmlWriter()". I guess the dot helps me to parse it as "factory's new XML writer", an interpretation allowed by the adjective "new" rather than the verb "create".

(It happens we're C++, so "new" also gives a clue to the unfortunately necessary memory management issues, but this may not be relevant.)

Post a comment