Motivation For Clean Code

Okan YÜKSEL
hepsiburadatech
Published in
7 min readMar 13, 2023

Regardless of the scale of the software project, we need to be part of any team, large or small. Therefore, it is very important that: the code, written by us, should be readable and developable by other developers.

The art of programming by Geek & Poke is licensed under CC BY 3.0

“Big” Dave Thomas, (founder of OTI, the godfather of the Eclipse strategy) defines clean code with these sentences:

Clean code can be read, and enhanced by a developer other than its original author. It has unit and acceptance tests. It has meaningful names. It provides one way rather than many ways for doing one thing. It has minimal dependencies, which are explicitly defined, and provides a clear and minimal API.

On the other hand, even if we are going to maintain the project alone, we may need to make additional developments for any module of the project months or years later. It is necessary to be able to understand, at a glance, what task the code written years ago fulfills, which modules of the project they serve, to protect the mental health of the developer, and to make sure that other modules are not affected by the new development.

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. Martin Fowler

Why Clean Code Is Needed?

It’s not hard to predict what’s going to happen when developing with unclean code. I want to talk about some of the problems we will face.

If the code is not cleanly;

  • Generates technical debt (If you want to take a look: “https://en.wikipedia.org/wiki/Technical_debt”). We can talk about managing technical debts under another heading, but there can be many reasons for technical debt. The first igniter of this wick is also non-cleanly code.
  • We encounter code blocks that become unmaintainable, over time the project may also become unmaintainable.
  • Would be costly to development. The development cost would be higher than it should be. If the dev team can’t foresee the cost, that’s a clear indication that we have a project that wasn’t cleanly developed.
  • Confusion grows. With each added feature, each code block developed, the confusion will grow. This means that the project dies over time. You may have heard a lot: “Let’s rewrite this.”

How To Get Clean Code?

First of all, we need the awareness of all members of the team and the institution that owns the project. Because achieving clean code will cost time. (money cost naturally, time = money)

We need to have the motivation to pair programming and refactoring with other members of the team.

We need to communicate further with the technical and commercial teams to make sure that we can simply explain and discuss the module or code block.

Clean coding is not a skill that can be acquired overnight. It is a habit that needs to be developed by keeping these principles in mind and applying them whenever you write code. Yiğit Kemal Erinç

Clean Code Frame

Simple, focused, completeness, correctness.

In order for us to accept that the clean code principles are followed;

  • The code should be simple and understandable.
  • Functions, classes and variables should be focused. They should be single responsible and not have side effects.
  • It should be able to respond to all needs, should cover possible cases, should allow the development and prevent situations that should be avoided.
  • We have to make sure it works correctly. This is only possible with unit and acceptance tests. Integration tests are required to test integrations.

Meaningful Names

The name of a variable, function, or class, should answer all the big questions. It should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent. Robert C. Martin

public List<string> GetTags(int[] a)
{
var list = new List<string>();

for (var b = 0; b < theList.Count; b++)
{
if (theList[b].CityCodes.Intersect(a).Any())
{
list.Add(theList[b].TagName);
}
}

return list;
}

Which tags will be returned from the GetTags() method? What is “a” parameter? What is “theList” variable? There are many uncertainties.

Without a comment or a deep dive of the code, it is impossible for me to understand what purpose this method and parameters serve. At this point, if I edit the naming on the condition that it makes sense in the context in which I am developing, I approach cleaner code. Let ‘s do it.

public List<string> GetTagNamesThatMatchedCityCodes(int[] cityCodes)
{
var tagNamesThatMatchedCityCodes = new List<string>();
foreach (var tag in locationTags)
{
if (tag.CityCodes.Intersect(cityCodes).Any())
{
tagNamesThatMatchedCityCodes.Add(tag.TagName);
}
}

return tagNamesThatMatchedCityCodes;
}

Now, the mission of this method can be understood at a glance. It’s easier to discuss in code review sessions because it has meaningful naming.

If we want to optimize performance especially for large datasets, we can also prefer the use of “yield return”, but this may reduce the readability of the code. In such quandaries, we may need to decide whether the readability or the performance of the program is important to us. For example, if I’m developing a compression algorithm, I want it to work efficiently. But if I’m developing a program for business and it won’t create too much system load, I can accept to lose some performance to have a readable and maintainable code.

public IEnumerable<string> GetTagNamesThatMatchedCityCodes(int[] cityCodes)
{
foreach (var tag in tags)
{
if (tag.CityCodes.Intersect(cityCodes).Any())
{
yield return tag.TagName;
}
}
}

Meaningful naming of variables, methods, and classes in the domain. This is another important point to strengthen communication within the team.

Developer, business analyst, product owner, etc. Names should be clear and understandable to every member of the team. Eric Evans defines the concept of “ubiquitous language” in his book “Domain-Driven Design: Tackling Complexity in the Heart of Software”, which is another book I would recommend to read if you get the chance.

The ubiquitous language is one of the key elements of dealing with complexity. This awareness will also prevent work accidents.

Short And Focused Functions

We should develop the functions as short and focused as possible. They should have only one mission (single responsibility) and they should do this mission to the best of their ability. This awareness will also prevent code duplication.

Methods with many lines of code increase complexity. Especially if we have an if statement or a code block containing loops, we can shorten the code block by making use of new methods to keep the code more understandable. This is an important topic in code review sessions to avoid increased complexity.

private void CreatePluralDependentMessageParts(int orderCount) 
{
if (orderCount == 0) {
FormatMessageForCustomerWithNoOrder();
} else if (orderCount == 1) {
FormatMessageForCustomerWithSingleOrder();
} else {
FormatMessageForCustomerWithManyOrders(orderCount);
}
}

Data Transfer Objects

Data transfer objects are a clean way to transfer data between application layers or functions.

I can talk about a scenario that will probably happen to you if you don’t use data transfer objects. As in the example below, the same type of method parameters come back to back, the developer trying to reach the deadline may confuse the string email and string appKey parameters. It won’t be long before you realize that your mail notification service has failed.

public Customer(string name, string surname, string email, 
string appKey, int age, bool isPremium)
{
Name = name;
Surname = surname;
Email = email;
AppKey = appKey;
Age = age;
IsPremium = isPremium;
}

If the method has more than a few parameters, it is time to combine these parameters into the data transfer object.

 public Customer(CustomerDto customerDto)
{
Name = customerDto.Name;
Surname = customerDto.Surname;
Email = customerDto.Email;
AppKey = customerDto.AppKey;
Age = customerDto.Age;
IsPremium = customerDto.IsPremium;
}

Comments

We should avoid writing comments. At first glance, we should be able to understand what purpose the code block serves. Correct naming and being focused are very important for clarity.

If the code is not clear, we should prefer code refactoring rather than commenting. However, comments can be made for situations that need to be explained.

// Check to see if the order is eligible for shipping
if (order.Payment.IsCompleted && order.Delivery.Slot.IsAvailable)

It should be better like this;

if (order.IsEligibleForShipping())

Use Searchable Names

Avoid using magic numbers in your code. Using searchable constants helps you approach cleaner code. Also, when you want to make changes in your business logic, you will be able to edit it effortlessly without having to scan the entire code.

if (password.Length > 10)
{
throw new ValidationException($"Must be at least 10 characters");
}
if (password.Length > MAX_PASSWORD_LENGTH)
{
throw new ValidationException($"Must be at least {MAX_PASSWORD_LENGTH} characters");
}

Error & Validation Handling

Sometimes we can find that the blocks of code written to process validation results or handle the error increase too much and make the code difficult to read. There should be a standard approach to error and validation handling throughout the application. And we have to make sure we don’t pollute the code base. To achieve this, we may prefer to throw an exception rather than carry error codes or flags. We can develop error handling middleware to log the exceptions and format our response.

 public bool IsValidPassword(string password)
{
if (string.IsNullOrEmpty(password))
throw new ValidationException("Cannot be null or empty");
if (password.Any(char.IsLetterOrDigit))
throw new ValidationException("Must contain at least one special character");
if (password.Length > MAX_PASSWORD_LENGTH)
throw new ValidationException($"Must be at least {MAX_PASSWORD_LENGTH} characters");

return true;
}

Finally, I would like to end with the following advice;

For the mental health of ourselves and our team, we should try to make our code better by avoiding irregular developments. Dirty code will cause more time wasted in the future and worse, very difficult problems to solve. As you stick to the clean code principles, you will realize that you develop better quality code, and the outputs of your developments will give you more happiness.

Thanks for your time.

Okan Yüksel, Software Engineer

References

--

--