This article is a compilation of knowledge and common practices that can help developers write better code. These practices can be applied to any programming language, framework or project.
The first section, “Code smell”, shows you what bad code can look like. Bad code may not be the wrong code, but it does show some potential problems in your software.
The second section, “Clean code”, shows you what good code looks like. (according to Uncle Bob).
The third section, “Refactoring”, introduces a techniques that are widely used in software development, an important technique in TDD. Refactoring helps you transform smelly code to clean code.
The final section, “Coding Convention”, gives you some tools and resources about coding convention for each language.
I. Code smell
According to Martin Fowler, “a code smell is a surface indication that usually corresponds to a deeper problem in the system”. Smells are certain structures in the code that indicate violations of fundamental design principles and negatively impact design quality. They are usually not bugs, they indicate weaknesses in the design that may be slowing down development, increasing the risk of bugs or failures in the future.
Detecting code smell is a skill required for all software engineer. Some tools can automatically check for certain kinds of code smell.
This is a short summary of common code smell, according to
Oddball solution: There should only be one way of solving the same problem in code.
Temporary field: Watch out for objects that contain a lot of optional or unnecessary fields.
Code smells between two classes:
Not just for OOP, but also for designing modules/structs:
Alternative Classes with Different Interfaces: Two classes are similar on the inside but different on the outside
Primitive Obsession: Don’t use a gaggle of primitive data type variables as a poor man’s substitute for a class. If your data type is sufficiently complex, write a class to represent it.
Data class: Avoid class that passively store data, it should also have methods to operate on that data.
Data clumps: If some kind of data always data hanging around together, maybe it belongs together. Consider rolling the related data up into a larger class.
Refused bequest: Inherit from a class, but never use any of the inherited functionality,
Inappropriate Intimacy: Watch out for classes that spend too much time together, or classes that interface in inappropriate ways. Classes should know as little as possible about each other.
Indecent Exposure: Beware of classes that unnecessarily expose the internals.
Feature envy: Methods that make extensive use of another class may belong in another class.
Lazy class:If a class that isn’t doing enough, can it be collapsed or combined into another class?
Message chains: Watch out for long sequences of method call or temporary variables to get routine data.
Middleman: If a class is delegating all its work, it should be removed. Beware classes that are merely wrappers over other classes or existing functionality in the framework.
Divergent change: If, over time, you make changes to a class that touch completely different parts of the class, it may contain too much unrelated functionality. Consider isolating the parts that changed in another class.
Shotgun surgery: If a change in one class requires cascading changes in several related classes, consider refactoring so that the changes are limited to a single class.
Parallel inheritance hierarchies: If every time you make a subclass of one class, you must also make a subclass of another, consider folding the hierarchy into a single class..
Incomplete library class: We need a method that’s missing from the library, but we’re unwilling or unable to change the library to include the method. The method ends up tacked on to some other class. If you can’t modify the library, consider isolating the method.
Solution Sprawl: If it takes five classes to do anything useful, you might have solution sprawl. Consider simplifying and consolidating your design
The code can be measured with either “good” or “bad” in the code review or by how many minutes it takes you to talk about it.
The code should be elegant, efficient, readable, simple, without duplications, and well-written.
You should add value to the business with your code.
Naming:
Should be meaningful and clearly indicate it does.
Should be pronounceable, related to the system domain, context
Avoid acronyms and avoid confusing names.
Replace magic numbers with named constants.
Method / Function:
Should be small. (< 20 lines, some believe that it should be < 10 lines)
Should only do one thing.
Small number of parameters: <= 2.
Always return something.
Shouldn’t create side effect.
Comment:
Explain what and why, rather than how.
Refactor the code to remove the comment if necessary. Prefer self-explanatory code to comment.
Comments can be used to express the importance of certain points in the code.
Formatting:
Set a limit of characters per line.
Use spaces between operators, parameters, and commas.
Similar functions should be close.
Declare variables close to their usage.
Class / Struct:
Should be small.
Do one thing.
Small number of instance variables.
Hide internal structure.
Base class should know nothing about their derivatives.
Prefer non-static methods to static methods.
Law of Demeter: one M method of an object O can only consume services of the following types of objects: The object itself, O; the M parameters; any object created or instantiated by M; direct components of O.
Unit test:
Follow the TDDs law:
Don’t create code before you have a failing test.
Don’t create more tests than necessary to fail.
You cannot write more code than enough to pass the test that is failing.
Refactoring is the process of restructuring existing computer code without changing its external behavior. Refactoring improves non-functional attributes of the software.
Refactoring is an important technique in TDD. More about TDD will come in another article.
Purpose of refactoring:
Improve readability.
Reduce complexity
Improve maintainability
Create a more expressive internal architecture or object model to improve extensibility.
In short: Transform from bad smell code to clean code
Techniques
There is a list of refactoring techniques that you can quickly read at:
. For details description, you can read the Refactoring book by Martin Fowler. The Refactoring to Pattern by Joshua Kerievsky introduces more techniques that are specific for object oriented patterns.
Our team uses various languages, including Javascript, Python, Ruby, Golang. Each language has its own set of recommended conventions. This section will introduce list of conventions for each languages and tools you can use to automatically check for these errors. Although each language has its own convention, they are still follow basic principle of clean code that has been described in the second section.