Good code – Bad code

A good product must be made from good materials.
And good software must be made from good code.

To know how to write a good code, we need to know what the bad code is.

Code Review
Code Review

What is bad code ?

Code is a communication method between programmers in a team regarding what we are doing, how we are doing it, and why we do it. Poor communication is a conversation that makes nobody understand or takes a month to comprehend. Bad code is code that leaves your whole team with no idea of what it is or why it is there. Communication is never about how much you can talk; it is about whether you can convey your idea effectively. Similarly, writing good code is never about which syntax you can use or what model you can apply; it is about making it clean and clear, meaning it should be easy for other programmers to understand.

Why is “hard-to-understand” code bad?

  • Code that is hard to understand means it will take a long time to fix, update, or change when new requirements come because people need time to comprehend and digest the code before they dare to make any changes. Even the authors of that code, after a few weeks, may not remember how it was made or why they made it that way. This reduces the adaptability of changes, which is the core meaning of the term Agile nowadays. When you can’t adapt quickly enough, you may be beaten by your competitors.
  • Code that is hard to understand means that when people make changes related to it, they potentially create bugs because there may be some magic in it that they don’t understand well. More bugs mean more time to fix. More time to fix means more cost for the development team. More cost means less effectiveness. Less effectiveness leads to blame. More blame means less happiness, and so on…
  • Code that is hard to understand can lead to other hard-to-understand code. Because it is hard to understand, and time is limited, a programmer has to make the last and also the worst choice: “hard code.” This hard code will bring many surprises later if there is no informing mechanism for others.

If your team is in situations where there are too many bugs, small changes in features can’t be accomplished in a short amount of time, or many developers blame each other, beware, your source code may have some issues.

What Are the Symptoms of Bad Code?

After a few years of programming, you can become a Senior developer, and reviewing Junior’s code will be your daily task. Reviewing code, by definition, ensures good code quality. But is it too arrogant to give someone the right to determine what is good? In fact, ensuring good code quality is about preventing bad code from making it into the product. To determine whether code is bad enough to be excluded, the Senior must be someone who has suffered through the pains of bad code and uses that experience to address every Junior’s question: “Why is it bad?” Below are some reasons that may help explain to people why the code is bad.

Before naming symptoms, let me remind you of the essence of coding. Coding, at its heart, in any language, is about defining states and changing defined states. For example, in Java, states are variables, and methods are used to update those variables; a collection of variables and related methods forms a class in Java. In JavaScript, there are functions and variables, and frameworks are essentially about how functions and variables are organized and wired together. Similarly, databases are used to store states permanently, and APIs, which every programmer knows, are about changing states stored in databases. Design patterns revolve around how to define states and how to wire logic—changing states properly for a single purpose: to make it easy to understand. Bad things happen when you don’t know how to wire things properly.

Below are some symptoms that I’ve seen from my work:

1. Decentralized logic

Logic is about changing states. If logic changing a state is located inside multiple components, it is decentralized. A component is a building block of the source code, like a class in Java, a function in JavaScript, an API, etc., depending on the scope. Most of the time, we want the logic to be atomic—meaning it shouldn’t fail partially. To make logic atomic, every piece of code related to it should be located in the same place, closely together, so that programmers always have a quick understanding of how states will be updated. Gathering code closely means having an easier and more intuitive rollback strategy. If pieces of code are split into multiple components, there are many chances that it will fail partially, and logic that fails partially causes bugs.

Decentralized logic also makes programmers spend more time finding and gathering information about states and how states are updated. If your programmers always have to search for usages of a particular variable or method throughout the project before daring to make updates on a particular feature or business logic, there is a chance that feature or logic is decentralized. Spend time centralizing them.

2. Out of pattern

The reason why development teams always want to use a particular framework is not only to reduce development time but also because frameworks contain proven patterns that help keep the source code clean, clear, easy to read, and scalable. Besides the strategic patterns defined by the framework, every team also defines their own tactical patterns like naming conventions, module division rules, etc. Every pattern brings its own reusable components, properly pre-wired to ensure that a small change requires only a few lines of code and a small amount of time. Out of pattern usually occurs when a programmer is not well-versed in those pre-defined rules. So, if you encounter logic that is unfamiliar compared to existing ones, or if there is significant effort in defining and wiring components, there is a chance it is out of pattern. However, sometimes current patterns can’t solve upcoming problems; defining a new one is acceptable, but this rarely happens.

3. Bad Naming

Engineers are bad at naming, and most of the time, poor naming makes other engineers confused. Have you ever found a class that contains a run(), an execute(), and a process() method? Or have you encountered variables like tmp_1, tmp_2, or entity1, entity2, etc.? Do you have any idea what they are about?

A variable name should describe its purpose so that you can tell other programmers what you intend to do with it without reading the whole function or understanding an algorithm just to know what it is. Bad naming usually comes from improperly defined components. We have a rule of Single Responsibility in programming: when a component holds more than one responsibility, it becomes hard to name it. Studying proven design patterns will help you generate ideas on how to divide responsibilities into components.

4. Repeat someone’s work

Actually, this happens because of poor communication between team members, so people don’t know what others are doing. The cost of this is likely that you are paying twice for one tool, and when the tool needs to be fixed, it costs twice as much too. There is a rule named “Don’t Repeat Yourself” in programming, but actually, it should be “Don’t Repeat Ourselves.”

5. Cumbersome solution

This is about the problem-solving skills of individuals. Some solve it in a tidy way, while others create chaos. However, it must be tidy. In pure algorithms like sorting and searching, a cumbersome solution may be the tradeoff for optimizing speed or memory. Nowadays, those algorithms are usually packaged into libraries. Most of the time, we deal with business logic, and if that business logic does not need to address memory optimization or speed enhancement, it should be simple. Clean code is preferable to clever code. The symptoms of a cumbersome solution can begin with poor naming in variables and methods, an excessive number of temporary items, or a multitude of loops and conditions compacted into one place.

6. Smell of the Hell

The term Hell in programming means “overusing.” Overusing anything is bad, right? We may have heard about terms like “callback hell” in the JavaScript world and “inheritance hell” in the Java world; let’s find out more about those hells by Googling it.

The cause of overusing things is that people don’t have a proper understanding of what they are using. If you are writing JavaScript and find that you have to chain more than two callbacks, it is time to find another approach, such as using Promises or the async/await function. If you are writing Java and discover a class that extends others but has to override too many non-abstract methods, there is something unclear in your class hierarchy. Another rule when dealing with inheritance is “Composition is over Inheritance.” A complex component is composed of simpler components, not built from a complex hierarchy.

7. Hard to document or express in paradigm:

Try to express the component and how components are wired together with others. If it is hard to express, there is a chance that the model is not clear enough.

How to avoid bad code ?

  • Learn Design Pattern : This give you some hints on how to design components and naming them properly
  • Review code seriously: This give you chances to sharing skills and knowledges, also have an overview about how the source code is growing.
  • Do thing tell people: If you write something reusable, tell people. If you have some note or document things somewhere, tell people. If you wanna know something, tell people.
  • Read official technical documents fully before actual do coding: this will provide you knowledge on existing solutions so you can avoid re-invent the wheel.

Build – Secure – Evolve with the-tech-lead.com

Leave a Reply