Blog 6 min read

Perfecting Code: Start with a Good Story

Share this article
Perfecting Code: Start with a Good Story

As software developers, we can write a lot of code each day. Each piece of code has its story, the code could be:

  • Inspired by a web resource (forum, tutorial, blog post,...)
  • Inspired by an open source project from GitHub, SourceForge or elsewhere.
  • Copy/pasted from the project itself.
  • Developed from scratch.

For each piece of code, the developer analyzes the problem to solve; his background and the opinions of his team can greatly influence his choices and the way the code is written.

After writing code and committing it, be sure that it will introduce debt that the project maintainer and developers will have to pay sooner or later. To minimize the debt and make the task easier for all project developers, it’s better to acquire some basic good habits to keep the code clean from the beginning.

1. Naming

Sometimes we spend a lot of time just trying to understand the purpose of a variable or a function because it’s named a, b or x. If it had been given a clear, meaningful name from the beginning, its meaning would be obvious.

Good, meaningful naming that follows a naming convention helps:

  • To reduce the effort needed to read and understand source code;
  • To enable code reviews to focus on more important issues than arguing over syntax and naming standards.
  • To enable code quality review tools to focus their reporting mainly on significant issues other than syntax and style preferences.

2. Visibility

Narrowing visibility is a good practice because it promotes encapsulation. Reducing the scope to a minimum helps the user of your code, who will know exactly which code can be used from outside a class.

Making all class methods public will confuse the user and hide the class contract; in that case, we need documentation to know which methods can be used.

3. Parameters

A function that takes more than five parameters indicates one of two problems:

  1. The function is doing too much. It should be split into several smaller functions, each with a smaller parameter set.
  2. There is another object hiding in there. You may need to create another object or data structure that includes these parameters.

There are some good gains to be had by doing this:

  • It makes your code easier to read.
  • It’s easier to unit test.
sqlite6

4. Size

Overly long methods are not easy to maintain and understand. Here’s some advice about the length of functions from the Linux coding style web page:

Functions should be short and sweet, and do just one thing.  They should
fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24,
as we all know), and do one thing and do that well.

The maximum length of a function is inversely proportional to the
complexity and indentation level of that function.  So, if you have a
conceptually simple function that is just one long (but simple)
case-statement, where you have to do lots of small things for a lot of
different cases, it's OK to have a longer function.
unreal44

5. Number of local variables

Methods where NbVariables is higher than 8 are hard to understand and maintain. Methods where NbVariables is higher than 15 are extremely complex and should be split into smaller methods (unless they are automatically generated by a tool).

sqlite7

6. Avoid defining complex functions

Many metrics exist to detect complex functions; NBLinesOfCode, number of parameters and number of local variables are the basic ones.

There are other interesting metrics to detect complex functions:

  • Cyclomatic complexity is a popular procedural software metric whose result is equal to the number of decisions that can be taken in a procedure.
  • Nesting Depth is a metric defined on methods, relative to the maximum depth of the most nested scope in a method body.
  • Max Nested Loops is equal to the maximum level of loop nesting in a function.

The maximum value tolerated for these metrics depends mostly on the team’s choices, as there are no standard values.

Let’s search for possible functions to be refactored:

sqlite8

7. Formatting

Programming style and indentation can be defined as the way you choose to organize and document your source code. Code indentation is a part of style and is mostly about aesthetics. If we follow a proper style guide and indentation, a program can be just like a POEM, and the reader will be comfortable enough to SAIL through it and understand its meaning. As we know, proper code indentation makes it:

  • Easier to read
  • Easier to understand
  • Easier to modify
  • Easier to maintain
  • Easier to enhance

The purpose of code indentation and style is to make the program more readable and understandable. It saves a lot of time when we revisit the code and reuse it. A style guide provides a road map that the developer should follow when coding, so that in a group of developers, all the code produced is consistent in nature and reusable by any developer.

8. Comments

Sometimes the code is not commented at all, and in other cases it’s over-commented. Maybe you’ve already read this phrase: Good code is self-documenting.

Yes, it’s good practice to keep the code clean and let it speak for itself, avoiding comments — but in the real world that’s not always easy. In some cases, you need to clarify what the code does.

9. Coupling

Low coupling is desirable because a change in one area of an application will require fewer changes throughout the entire application. In the long run, this could alleviate a lot of time, effort, and cost associated with modifying and adding new features to an application.

Functions that use many other functions are very difficult to understand and maintain. It’s recommended to minimize the efferent coupling of your functions.

sqlite10

10. Cohesion

The single responsibility principle states that a class should not have more than one reason to change. Such a class is said to be cohesive. A high LCOM value generally pinpoints a poorly cohesive class. There are several LCOM metrics. LCOM takes its values in the range [0-1]. LCOM HS (HS stands for Henderson-Sellers) takes its values in the range [0-2]. An LCOM HS value higher than 1 should be considered alarming. Here is how to compute the LCOM metrics:

LCOM = 1 – (sum(MF)/M*F) LCOM HS = (M – sum(MF)/F)(M-1)

Where:

  • M is the number of methods in the class (both static and instance methods are counted; it also includes constructors, property getters/setters, and event add/remove methods).
  • F is the number of instance fields in the class.
  • MF is the number of methods of the class accessing a particular instance field.
  • Sum(MF) is the sum of MF over all instance fields of the class.

The underlying idea behind these formulas can be stated as follows: a class is utterly cohesive if all its methods use all its instance fields, which means that sum(MF)=M*F, and then LCOM = 0 and LCOMHS = 0.

LCOMHS value higher than 1 should be considered alarming.

unreal36

Conclusion

These are some basic habits that can keep your code clean from the beginning; don’t wait for a refactoring to clean up your code — try to do it from the start.

Share this article