Tuesday, October 14, 2008

Easy To Read Code

If we wanted to write code for a computer, we would be using only 0s and 1s, but we don't. Code is written for human consumption. It is more important that a co-developer can easily "compile" the code than any machine. If any code contains a bug, a machine isn't going to fix it - only a human will. Therefore we should take extra care to make the reader's job as easy as possible by making the code as easy as possible to read. Any code worth writing is worth writing well.

Unlike a computer, we can't read one line at a time. Our natural field of focus has a limited width and this needs to be considered when writing and creating APIs. This courtesy leads coders to limiting the length of each line. The easiest way to correct long lines of code is to use more local variables to allow operations to be separated on separate lines. However, too often this is not enough.

Often libraries use overly verbose and repetitive names. This is done in the name of clarity, but at the expense of the readability of their users' code. API developers need to keep not only the readability of their own code in mind, but also the readability of their users' code. Here are four examples that I have seen recently, of APIs that force their users into writing difficult to read code:
  • ServletConfig#getServletContext() - The word Servlet is redundant in the method and could be removed to shorten calling code.
  • org.openrdf.repository.RepositoryConnection - Unless it is common to work with multiple connections from different packages, there is no need to repeat the word "repository" in the package and class prefix.
  • IReadableBinaryStreamRepresentation - Here is an example of a repeating suffix that adds nothing to clarify its use.
  • context.getKernelContext().getThisKernelRequest().getRequestScope() - If the API forces users in this type of repetitive message chaining, not only is the API forcing code that is hard to read, but it also couples the client to the structure of the navigation and any changes to the intermediate relationships forces the client to also change.

To any API designers (or would be API designers) out there: Please spend some time thinking on how you can reduce the repetition in your API and strive to use short concise names. It will go a long way to making more readable code.

Reblog this post [with Zemanta]

6 comments:

  1. Hi James,

    You haven't offered any alternatives to the examples that you give.

    Also, while I agree that striving for concise names is a good thing, the documentation provided by longer names is often extremely useful.

    Also, I don't see the redundancy in the 3rd example. The class in question is a distinct type of application domain specific entity called a "Representation". It's a representation of a binary stream. There is already a class named BinaryStreamRepresentation but this one is distinct because the stream content is Readable. So, the name is long but not redundant within this API.

    As to the 4th example, this is not a naming problem per se but could indicate that a class refactoring might be warranted if this is an "idiom" that occurs over and over.
    regards,
    -tom

    ReplyDelete
  2. Hi Tom,

    In the third example, I would suggest using the name "IReadableBinaryStream". All objects in one way or another are representations of something and I don't think think adding it as a suffix adds any more clarity. I realise that in this case representations provide a unique behaviour, but (with my understanding) I don't expect any collisions.

    Message chaining came up in Virginia wrt this API and although this might be an extreme example, I still think it contains unnecessary chains.

    ReplyDelete
  3. I agree that, in general, all objects are representations of something but, in this case, that is not what is meant here.

    In this particular API, a Representation is an application domain entity, just as an Invoice or a LineItem might be in a billing application. The class in question is NOT a readable binary stream; but an instance of a Representation which happens to hold a readable binary stream.

    So, while "representation" might be redundant in some other API, here it isn't any more redundant than the use of 'Invoice' in 'PreferredCustomerInvoice', a name which could not be shortened to 'PreferredCustomer' without distortion of meaning.

    ReplyDelete
  4. I don't mean to claim full understanding of the API and can only given limited opinions, but the class name, as printed, does not roll off the tongue as easily as perhaps it could.

    The second example might also be bit long because it is often simply referred to as a "connection" in conversation, therefore the prefix is redundant since it is usually omitted during conversation.

    However, because Java does not give us any way to locally reference a package, except by full name, a prefix maybe necessary to avoid confusion. For example RepositoryConnection and SailConnection. This maybe the case with the third example (I don't know).

    ReplyDelete
  5. "...but the class name, as printed, does not roll off the tongue as easily as perhaps it could."

    That's for sure! It's definitely one of the longer names I've encountered in this API.

    It's still nowhere near as long as some of the names in the Spring API, however. :) Like....

    DelegatePerTargetObjectIntroductionInterceptor
    IncorrectUpdateSemanticsDataAccessException
    JdbcUpdateAffectedIncorrectNumberOfRowsException
    PersistenceExceptionTranslationPostProcessor
    TransactionAwarePersistenceManagerFactoryProxy

    Just to name a few...

    ReplyDelete