Clean Code Tip 2: Write readable code

What is meant by readable code? In essence, it refers to code that resembles the English language as closely as possible.
This emphasis on English arises because it is the language most programmers understand best. Most programming languages are designed to allow code that closely resembles English. Each programming language has its own syntax, which must be learned to read code effectively in that language. These syntaxes are generally concise and relatively straightforward to master. Programming language keywords are typically clear English terms such as if, then, else, for, while, and return. Therefore, coding in a language other than English can result in code that appears inconsistent, combining English keywords with identifiers from another language. We can summarize:
Code should read like well-written English prose.
Code should be readable, not impressive. Try to avoid clever one-liners, such as nested ternary operators, and use clear, multiline logic instead.
Let's have a couple of examples:
// Golang
if x % 2 == 0 {
return "Even"
}
return "Odd"
// C++'s ternary operator
return x % 2 == 0 ? "Even" : "Odd";
// Python's conditional expression
return "Even" if x % 2 == 0 else "Odd"
Among the three examples presented, the first is likely the most readable for the majority of programmers. The last two examples, often referred to as one-liners, may be more challenging to interpret, particularly for beginners or those unfamiliar with the language. Conversely, experts in the specific programming language may find the latter examples more readable due to their familiarity with the syntax and the concise nature of the code, which can reduce reading time. Therefore, readability is not an absolute measure; it varies according to the reader's expertise. When uncertain about which code style to use, it is advisable to select the version that is most accessible to a broad range of programmers.
Sometimes, code that is too wordy can make it harder to read. Long variable, function, or class names take more time to read and understand. Good readability means finding a balance between names that are too short and those that are too long. For example, if you have a variable that stores a configuration object, calling it configuration is clear and not too long. You don't need to shorten it to config, conf, cfg, or just c. But if your variable or function names get close to 20 characters, or your class names reach 30 characters, it's a good idea to use abbreviations. Otherwise, your code may become hard to read. For instance, a variable named applicationConfigurationParser is 30 characters long and can be difficult to read, especially if it appears often. You could shorten it to appConfigParser instead:
applicationConfigurationParser.parseApplicationConfiguration(...);
vs.
appConfigParser.parseAppConfig(...);
Use abbreviations only when a well-known form exists. For instance, app is widely recognized for application, and config for configuration. In contrast, prsr for parser and dscnt for discount are not widely accepted.
Current programming languages do not allow code to be written in a manner that precisely mirrors the English language. This limitation arises because functions must be invoked in a specific format:
returnValue = myFunction(parameter1, parameter2, ...)
For example, you write:
sortedArray = array.sort()
“array sort" is not correct English usage. The words should be rearranged to form "sort array" for proper English syntax. In such cases, it is useful to conceptualize the process as sending a sort message to the array object. In this example, the array functions as the object rather than the subject of the sentence.
In some cases, adding prepositions can improve the readability of a function call:
final var configString = readConfig(url)
In the example above, the code can be interpreted as if the function name ends with the word "from", resulting in the phrase: "read config from url".
Or:
final var frenchSentence = translate(englishSentence, Language.FRENCH)
In the preceding example, it is necessary to insert a 'to' preposition between the function arguments and adjust the word order in the enum constant so that the function call reads: "translate English sentence to French language."
Sometimes you need to add a verb to a function call and put the function parameter in the middle of the function name:
numberString = ToString(number)
"ToString number" is not English, but "convert number to string" is.
Consider an example involving a C++ application that consumes messages from a source system, decodes them, transforms them, encodes the transformed data, and finally produces a new message for a destination system:
void DataExporterApp::run()
{
while(m_isRunning)
{
const auto inputMessage = m_messageConsumer.consumeMessage();
const auto internalMessage = m_messageDecoder.decode(inputMessage);
const auto transformedMessage = m_messageTransformer.transform(internalMessage);
const auto outputMessage = m_messageEncoder.encode(transformedMessage);
m_messageProducer.produce(outputMessage);
}
}
The code presented is straightforward and maintains a clean structure. However, certain elements, such as the repeated use of const auto, introduce unnecessary verbosity without contributing substantive value.
Let's rewrite the previous code using a hypothetical programming language that allows code to be expressed in language closely resembling plain English:
Application :: run:
do until application is stopped:
Message consumer consumes a message.
Message decoder decodes that message to an internal message.
Message transformer transforms the internal message to a transformed message.
Message encoder encodes the transformed message to an output message.
Message producer produces the output message.
Application :: create:
Message consumer is Apache Kafka message consumer.
Message decoder is Avro binary message decoder.
Message encoder is CSV message encoder.
Message producer is Apache Pulsar message producer.
Application :: application is stopped:
Is application state stopped?
Application :: stop:
Set application state to stopped.
The code above demonstrates greater readability compared to the C++ example. It is also accessible to many non-technical stakeholders, such as product owners and product managers. In this approach, a variable is declared using the indefinite article a or an, and subsequently referenced with the definite article the. By default, all variables are constant, similar to the convention in the Rust programming language. To define a mutable variable, this must be explicitly stated in the code, for example:
message decoder decodes that message to a mutable internal message.
The code can be displayed with specific highlighting in the integrated development environment (IDE) to distinguish variables, incoming function parameters, outgoing function parameters (return values), and their respective types. By examining the dependencies instantiated in the Application class constructor (create method), it becomes evident that the abstract code in the run method reads Avro binary messages from Apache Kafka, encodes them to CSV format, and transmits them to Apache Pulsar.
If you are interested in reading more about clean code, grab yourself a copy of my 140 Tips For Clean Code booklet.




