Clean Code: Writing Functions or Methods

"Refactoring changes the programs in small steps, so if you make a mistake, it is easy to find where the bug is".

Pabashani Herath
The Startup

--

This article is the second of a clean code series, you can find the first part about Clean Code Naming Conventions. Let’s jump in and learn how to code clean functions!

The quality of code is critical if the application is to be stable and easily extensible. Apparently nearly every developer, including myself, faced low-quality code in his career. So they’re a marsh. That kind of code has the following negative properties:

  • Functions are too lengthy and do too much
  • Functions also have side effects that are difficult to comprehend or even test
  • The naming of functions and variables are vague
  • Fragile code: a small improvement unexpectedly damages certain components of the program
  • Bad or incomplete coverage of the code

If you hear phrases like: “I don’t understand how this code works”, “this code is a mess”, “it’s hard to modify this code” and, that means your code has these negative properties.

This article covers several best practices on how to write plain, understandable, and easy to test functions. Those are:

1. Functions should be small !!! Really
2. Block and indenting
3. Do one thing! (Single Responsibility Principle)
4. Function names should say what they do
5. Remove duplicate code
6. Avoid Side Effects
7. Remove dead code

1. Functions should be small !!! Really

“The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.” — Robert C. Martin

The first rule of writing functions is functions should be small. The second rule for writing functions is that they should be even smaller. Functions that have 200 or 300 lines are kind of messy functions. They should not be longer than 20 lines and mostly less than 10 lines. Functions should be making as few arguments as possible, ideally none. Small functions are easy to understand and their intention is clear.

“Coding like poetry should be short and concise.”

― Santosh Kalwar

There are some important rules in Refactoring in Large Software Projects by Martin Lippert and Stephen Roock. According to this, there are 2 more rules related to methods/functions. They are:

They should not have more than an average of 30 code lines (not counting line spaces and comments).

A class should contain an average of fewer than 30 methods/functions, resulting in up to 900 lines of code.

Many people are so enamored of small functions that a strongly promoted idea of abstracting any and every piece of logic that may even are nominally complex into a separate function is something.

As an example:

function greetings() {
console.log(“Hello World”);
}
// calling
greetings();

There is a Visual Studio Code Extension ("CodeMetrics") which can automatically calculate the complexity in TypeScript / JavaScript / Lua files.

If You want to know the causes You can click on the code lens to list all the entries for a given method or class. (This also allows You to quickly navigate to the corresponding code)

2. Block and indenting

The rule of keeping the functions small also implies that we keep the code blocks inside if, else and while statements minimal, preferably up to one line and that one line should be a function call. Similarly, we should avoid nesting such structures inside functions by moving the nested logic into another function.

Don't use long if, else, switch and while statements, because sometimes they make functions too big and messy.

If it is necessary to use if, else, switch and while statements, try to use only one or two lines and that line should be a function call.

As below example;

function greetings(timePhase:string) {
if (timePhase === “morning”) {
console.log(“Good Morning”);
} else if (timePhase === “afternoon”) {
console.log(“Good Afternoon”);
} else if (timePhase === “evening”) {
console.log(“Good Evening”);
} else {
console.log(“Good Night”);
}
}
// calling
greetings();

You can write this;

function printGreeting(greeting:string) {
console.log(greeting);
}

function greetings(timePhase: string) {
let greeting = ‘Good’ + timePhase;
printGreeting(greeting);
}

According to this example, it is simple, small, and easy to read and understand the code.

3. Do one thing! (Single Responsibility Principle)

“Functions should do one thing. They should do it well. They should do it only”.

DOTADIW: Do One Thing and Do It Well — Unix philosophy

One function must do only one thing. If a function is doing more than one thing, the function should be split up into more functions.

The Curly Rule, Do One Thing, is expressed in many key concepts of modern software development:

  1. Don’t Repeat Yourself
    If there is more than one way to express the same thing, it is likely that at some point the two or three different representations will be out of step. But if they don’t, you’ve guaranteed that if a transition happens, you have them in tandem. And a change is going to occur. Do not repeat yourself is important if you want flexible and maintainable software.
  2. Once and Only Once
    Both statements of conduct take effect once and only once. This is one of the key purposes when the code is restored if not the main goal. The design aims to eliminate duplicated behavioral statements by merging or replacing them with a unifying abstraction.
  3. Single Point of Truth
    Repetition leads to confusion and subtly broken codes because only a few repetitions were updated as expected. Sometimes that also means you didn’t think properly of your code. When you see duplicate code, this is a danger sign. Integrity is a cost, do not pay twice.

A class should have one and only one justification to alter, the Single Responsibility principle (SRP). To put this differently, for the same reasons the practices of a class should change; different factors that change at different levels should not be affected.

4. Use Descriptive Names

A function‘s name should explain what exactly it is doing. Nothing less, no more. Functions with names like “DO,” “ACTION,” for example, are not very useful, because we don’t know what their reach is.

The name of a variable, function, or class, should answer all the big questions. It should tell you;

  1. Why it exists?
  2. What does it do?
  3. How it is used?

If a name requires a comment, then the name does not reveal its intent.

Remember Ward’s principle: “You know you are working on clean code when each routine turns out to be pretty much what you expected.

Half the difficulty for this is for good names to be selected for little roles. The smaller a feature is and the more centered the simpler a descriptive name is to pick.

Don’t be scared about having a long name. It is easier to have a long descriptive name than a brick name. A name with a long description is better than a shorter description. Use a naming convention that allows multiple terms to be read easily in feature names, and then use the multiple words to name a feature.

Don’t be afraid to spend time choosing a name. Indeed, you should try several different names and read the code with each in place. Selecting descriptive names will help you clarify the module’s nature and develop it. It is not rare for a code to be restructured favorably by looking for a good name.

A few examples of good descriptive method names:

  • WriteToOrderXmlFile()
  • CreateNewCustomer()
  • CreateDeduplicatedListOfShippingContacts()
  • GetListOfPartProductsNotInSalesforce()
  • CreateNewPartProduct()

You can read my first article regarding naming conventions (Clean Code Naming Conventions) to get a better idea.

5. Remove duplicate code

Make every attempt to prevent duplication of code. Duplication of the code is bad since it means that if you need to change a concept, there is more than one place to change things.

Just imagine running a restaurant and keeping an inventory track: all your tomatoes, onions, garlic, spices, etc. If you have several lists, everything must be revised when you serve a dish containing tomatoes. There is only one place to update if you have only one list!

Bad code:

public void method() {
int a = 1;
int b = 2;
int c = a+b; // duplicate
int d = b+c; // duplicate
}
...
private int add(int a, int b) {
return a+b;
}

After removing duplicate codes:

public void method() {
int a = 1;
int b = 2;
int c = add(a,b);
int d = add(b,c);
}
...
private int add(int a, int b) {
return a+b;
}

6. Avoid Side Effects

If a function does nothing other than entering a value and return other values or values, it creates a side effect. A side effect may be to write to a file, change a global variable, or by mistake to a stranger.

Today, in the software you may also have side effects. You will need to write in a file, as in the previous example. It’s centralizing where you want to do this. Don’t have several features and classes to write a file. Have a program to do it. Just one. Just one.
Side effects are mutations or actions that happen in our code environment that we cannot make an account of. It could result in pollution of the global scope.

Bad Code:

// Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
let name = "Ryan McDermott";

function splitIntoFirstAndLastName() {
name = name.split(" ");
}

splitIntoFirstAndLastName();

console.log(name); // ['Ryan', 'McDermott'];

Good Code:

function splitIntoFirstAndLastName(name) {
return name.split(" ");
}

const name = "Ryan McDermott";
const newName = splitIntoFirstAndLastName(name);

console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];

7. Remove dead code

Dead code is as poor as double code. There’s nothing in the coding base to hold it. Get rid of it if it’s not named! If you ever need it, it will remain secure in your version history.

Nobody had the time to clean up the old code when the program specifications changed or corrections were made.

The code can also be found in complicated situations if one of the branches (due to error or other circumstances) becomes inaccessible.

Visual Code extension (ESLint) can analyze your code to quickly find problems such as unused variables, functions, and classes, etc.

Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers.

Bad Code:

/*eslint no-unused-vars: "error"*/
/*global some_unused_var*/

// It checks variables you have defined as global
some_unused_var = 42;

var x;

// Write-only variables are not considered as used.
var y = 10;
y = 5;

// A read for a modification of itself is not considered as used.
var z = 0;
z = z + 1;

// By default, unused arguments cause warnings.
(function(foo) {
return 5;
})();

// Unused recursive functions also cause warnings.
function fact(n) {
if (n < 2) return 1;
return n * fact(n - 1);
}

// When a function definition destructures an array, unused entries from the array also cause warnings.
function getY([x, y]) {
return y;
}

Good Code:

/*eslint no-unused-vars: "error"*/

var x = 10;
alert(x);

// foo is considered used here
myFunc(function foo() {
// ...
}.bind(this));

(function(foo) {
return foo;
})();

var myFunc;
myFunc = setTimeout(function() {
// myFunc is considered used
myFunc();
}, 50);

// Only the second argument from the descructured array is used.
function getY([, y]) {
return y;
}

Conclusion

The basic building blocks of any system are functions. These are device verbs, while classes are the substantives. This post focused on writing the functions so that they can be interpreted, understood, updated, and revised. It should be applied when taking into consideration the idea of DRY (Don’t Repeat Yourself). You would have descriptive, pure, and beautifully structured functions by following the above concepts and practices.

Previously they come out long and complicated when I write functions. They have plenty of nesting loops. You have long lists of arguments. The names are arbitrary and the code is doubled. However, now I like to refactor my code according to the standards.

It’s hard to think of those who apply business logic. There are plenty of guidelines to follow. No one can get their first attempt all right. Functions are like writing a story. There is a first draft, a second draft … and a final draft likewise, there can be a lot of drafts. Don’t be afraid to compose long, messy functions. When it works, improving it and restoring it until it sounds like a Harry Potter novel.

When you are writing clean functions with descriptive names, you better to read my first article regarding naming conventions;

“Of course bad code can be cleaned up. But it’s very expensive.”
-Robert C. Martin-

--

--