What is self-documenting code?
Self-documenting code is code that is written in a way that is easy to understand and that conveys its purpose and function without the need for extensive documentation. This can be achieved through the use of clear and descriptive variable names, well-structured code, and comments that explain the purpose and function of different parts of the code.
Self-documenting code can be helpful for a number of reasons. It can make it easier for other developers to understand and work with your code, especially if they are unfamiliar with it. It can also make it easier for you to understand your own code if you need to come back to it at a later date. Additionally, self-documenting code can reduce the amount of time and effort required to maintain and update the code, as it is easier to understand what needs to be changed or fixed.
To write self-documenting code, it is important to focus on making your code clear and easy to understand, using descriptive variable names, and adding comments to explain the purpose and function of different parts of the code. By doing this, you can help to ensure that your code is easy to understand and maintain, even if you or other developers are not familiar with it.
Does self-documenting code negate the need for documentation altogether? Not at all! It can mainly significantly decrease the need for inline comments, but that is still worthwhile.
The article is part of a series of articles about documentation tools.
A simple example of self-documenting code
Here is an example of a simple order booking mechanism written in Python with code that is not self-documenting:
def book_order(items, qty, price):
total = 0
for i in range(len(items)):
total += qty[i] * price[i]
return total
print(book_order([“item1”, “item2”, “item3”], [2, 3, 4], [10, 20, 30]))
This code takes a list of items, a list of quantities, and a list of prices, and calculates the total cost of the order. However, the code is not self-explanatory, as it uses short and unclear variable names and does not include any comments or documentation.
To improve the readability and self-documentation of this code, we can use clear and descriptive variable names, and add reusable functions to break down the logic of calculating the total cost into a separate, reusable function:
def calculate_total_cost(item_names, item_quantities, item_prices):
total_cost = 0
for i in range(len(item_names)):
total_cost += item_quantities[i] * item_prices[i]
return total_cost
def book_order(item_names, item_quantities, item_prices):
return calculate_total_cost(item_names, item_quantities, item_prices)
print(book_order([“item1”, “item2”, “item3”], [2, 3, 4], [10, 20, 30]))
Tips from the expert
5 tips to make your code self-documenting
1. Organized naming
Using organized and descriptive names for variables, functions, and other code elements is a best practice for writing self-documenting code because it helps to make the code more readable and easier to understand.
When you use clear and descriptive names, it becomes much easier for other developers (or even for yourself, if you come back to the code at a later date) to understand what the code is doing and how it works. For example, using a variable named “customer_name” is much clearer than using a variable named “x,” as it immediately conveys the purpose and meaning of the variable.
Using organized naming is also a good way to help enforce a consistent style and structure in your code. By following naming conventions and standards, you can help make your code more predictable and easier to read.
Overall, using organized and descriptive names is an important best practice for writing self-documenting code because it helps to make the code more readable and easier to understand, which can save time and effort when working with the code.
2. Syntax
Using syntax methods like reusable functions, replacing conditional statements with functions, and turning expressions into variables is a best practice for writing self-documenting code because it can help to make the code more readable, maintainable, and efficient.
Reusable functions, in particular, can help to make your code more self-documenting by encapsulating complex logic or repetitive tasks into discrete, self-contained blocks of code. By giving the function a clear and descriptive name, you can convey the purpose and behavior of the function, which can make it easier for other developers to understand how it works.
Replacing complex conditional statements with functions can also help to make your code more self-documenting by breaking down complex logic into smaller, more manageable chunks. By giving the function a clear and descriptive name, you can convey the purpose and behavior of the function, which can make it easier for other developers to understand how it works.
Turning expressions into variables can also help to make your code more self-documenting by giving names to complex or obscure expressions. This can make the code more readable and easier to understand, especially if the expression is used multiple times throughout the code.
3. Structural clarity
Having a clear and logical structure to your code can make it easier for other developers (or even for yourself, if you come back to the code at a later date) to understand how the different parts of the code fit together and work together. This can be achieved through the use of clear and descriptive names for files, modules, and classes, as well as through the use of comments and documentation to explain the purpose and behavior of the code.
Having a clear and logical structure to your code can also help to make it more maintainable and easier to modify or extend over time. By keeping related code together and separating unrelated code, you can help to reduce the risk of introducing errors or unintended side effects when making changes to the code.
4. Avoid using break
Avoiding the use of “break” statements is generally considered a best practice for writing self-documenting code because it can help to make the code more readable and easier to understand.
The “break” statement is used to exit a loop or switch statement prematurely. While it can be useful in certain situations, it can also make the code more difficult to understand if used excessively or in a way that is not immediately clear.
For example, if you have a loop that contains multiple “break” statements at different points, it can be hard for other developers to understand the logic of the loop and how it is intended to work. This can make the code more error-prone and harder to maintain.
In general, it is better to use other control flow structures like “continue” or “return” to exit a loop or switch statement, as these can be easier to understand and more self-documenting.
5. Avoid magic numbers
Avoiding magic numbers is a best practice for writing self-documenting code because it can help to make the code more readable and easier to understand.
A magic number is a numeric literal that appears in the code without any explanation or context. For example, consider the following code:
if (age < 18) {
print(“You are not old enough to vote.”);
}
In this example, the number 18 is a magic number because it appears in the code without any context or explanation. It is not immediately clear why the number 18 is significant or what it represents.
Using magic numbers in your code can make it more difficult for other developers (or even for yourself, if you come back to the code at a later date) to understand the logic and purpose of the code. It can also make the code more error-prone, as it can be easy to mistype or forget the significance of a magic number.
To avoid magic numbers, it is generally best to use constants or variables to represent significant numeric values. For example:
const int MIN_VOTING_AGE = 18;
if (age < MIN_VOTING_AGE) {
print(“You are not old enough to vote.”);
}
In this example, the constant MIN_VOTING_AGE clearly represents the minimum age at which a person is eligible to vote. This makes the code easier to read and understand, and it reduces the risk of errors or misunderstandings.
Does self documenting code mean no need for documentation?
When code is well written, it’s easy to gain an understanding of it in isolation. However, even clean code doesn’t tell the whole story. Design decisions, business logic, or the underlying reasons behind not implementing something in a certain way, just don’t appear in the code, no matter how “clean” it is.
Clean code is definitely important and it should be easy to understand in isolation. But, developers are faced with trying to understand the full picture of the code. Not just a specific line or function, but the links that exist between them. This becomes more important as projects become more complex, spanning across multiple microservices and repositories, perhaps even implemented in different programming languages.
Use Swimm to make your code even clearer
At Swimm, we built a knowledge management tool for code. We allow engineering teams to share knowledge about their codebase, so that the documentation is kept up to date, and that engineers find it when they need it.
With Swimm, developers create walkthrough documents – engaging cross-repository docs with content components (e.g., variables, tokens, file/folder paths, code snippets, diagrams) that interact directly with the code and are automatically synchronized as changes are made. These docs are especially helpful in explaining the broad flows spanning across multiple files, modules and repositories – exactly where clean code is not sufficient.