Ten Rules to Refactor Code

Ten Rules to Refactor Code

·

4 min read

DISCLOSURE: This article contains affiliate links to external products. This means that I will earn a commission at no additional cost to you if you decide to make a purchase. Thank you!

I want to dive deeper into code refactoring in this blog post. I will refer to the book "Five Lines of Code" by Christian Clausen.

image.png

I particularly enjoyed the book because the author takes a different approach to refactoring code than the one usually explained in terms of "code smells".

What are the rules for refactoring code?

The author maps out some rules that must be put in the proper context to maximize their benefits.

Let's see what these rules are.

#1 RULE: A method should not have more than five lines

An extended method can be classified as a "code smell".

This rule prevents a method from doing too much.

If a method does too many tasks, it's best to break it down into smaller methods.

#2 RULE: Either call or pass

A method should do two things:

  1. Call methods on an object.

  2. Pass the object as an argument.

Not both.

image.png

  • sum(arr) is the high-level abstraction.

  • arr. length is the low-level abstraction.

  • In example 3.15, the calculation follows a high-level abstraction only.

#3 RULE: Never use switch statements

Switch statements tend to be used a lot, especially by beginners.

The problem with them is that:

  • You don't always have to do something for each value we set (indeed, they support default).

  • It's easy to forget a break statement in between values.

Avoid putting functionalities into default clauses.

#4 RULE: Only inherit from interfaces

Usually, you inherit from classes or abstract classes.

However, you should only inherit from interfaces because inheritance promotes tight coupling when sharing code between classes.

In addition, duplicated code is challenging to maintain.

#5 RULE: No interfaces with only one method

You shouldn't have interfaces that only have one implementation.

This helps us reduce boilerplate code.

#6 RULE: Avoid using getters and setters

Getters and setters are how you achieve encapsulation.

You make the properties private but the getters and setters public.

There is a problem with this, though:

  • With getters, anyone who gets the object can call its public methods, which means that we can possibly change its behaviour in a way that you don't expect.

  • With setters, you risk introducing an additional layer of indirection where you can change the internal data structure.

To overcome this, rethink the application's architecture as "push-based".

A push-based architecture is a type of architecture where you pass data as arguments.

This way, all classes end up having functionalities.

#7 RULE: Never have common affixes

Often, engineers enjoy using affixes to name variables.

An example of this can be:

String endTime;
String endDate;

The problem with using affixes to name variables is that it can lead to coherence.

Use classes instead because they give you more control over the external interface.

#8 RULE: if only at the start

if statements should always be at the top of a method.

Nothing else should happen after the if statement.

This doesn't mean you should separate the If from the else (they go together).

Extract the rest into a separate method if you need to perform anything else after an if statement.

#9 RULE: Never use If with else

You should not use if with else unless you check against a data type.

Using if...else statements make the code more rigid because we're saying that a decision has to be made at a specific point.

The author refers to if...else statements as early binding, a code smell.

When we compile our program, a behavior—like if-else decisions—is resolved and locked into our application and cannot be modified without recompiling

The contrary to early binding is late binding.

The opposite of this is late binding, where the behavior is determined at the last possible moment when the code is run

Use objects instead, which allows for more flexibility.

#10 RULE: Use pure conditions

A pure condition is a condition that doesn't provoke any side effects.

A condition that provokes side effects means assigning values to variables, throwing exceptions, etc.

You should avoid this by separating getting data from changing data.

Related: What is "Code Refactoring"?

What makes an application easy to break?

An application becomes fragile when changing something in one place causes a break in another place.

A cause of this fragility is the global state.

"Global" means that something is outside of the scope.

"State" refers to anything that can change when running a program.

Whenever you make properties global, they become exposed to misuse.

All the properties you don't want to check in the code explicitly are known as invariants.

Localizing invariants means that things that change together should stay together.

Conclusion

After reading this article, you have learned about the top ten rules to refactor code.

The book also contains:

  • Refactoring patterns.

  • Game-based application of the refactoring rules explained above.

And much more. Code snippets are in Typescript.

Get the book on the Manning Publications website.

I hope you've found this article helpful.

Until next time! 👋🏾

Did you find this article valuable?

Support Maddy by becoming a sponsor. Any amount is appreciated!