Good code – Bad code

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.

What is bad code ?

Code is a communication method between programmers in a team on what we are doing, how we are doing it and why we do it . A bad communication is a conversation that make nobody understand, or take a month to understand. A bad code is a code that make your whole team have no idea what it is or why it was there. Communication is never about how much you can talk, it is about whether you can make others get your idea. And similarly, writing good code is never about which syntaxes you can use, what model you can apply, it is about whether you can make it clean and clear – it means to be easy to understand to others programmers.

Why is “hard to understand” code bad ?

  • Code that hard to understand means it will take long time to fix, update, or change when new requirement comes because people need time to consume and digest the code before dare to make some changes. Even the authors of that code, after a few weeks, may not remember how it was made or why he 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 quick enough, you may be beaten by your competitors.
  • Code that hard to understand means when people make some changes related to it, they potentially create bugs because there are maybe some magic in it that they don’t understand well. More bugs means more time to fix. More time to fix means more cost for development team. More cost means less effectiveness. Less effectiveness leads to blames. More blames less happiness, and so on…
  • Code that hard to understand can leads 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, is “hard code”. And that hard code will bring many surprises lately if there is no informing mechanism to others.

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

What are symptoms of bad code ?

After a few years of programming, you can be a Senior guy and reviewing Junior’s code will be your daily task. Reviewing code, as its definition is to ensure good code quality. But is it too arrogant when give someone the right to tell what the good is ? Actually, to ensure good code quality is to prevent bad code to come to the product. To be able to tell whether a code is bad enough to be prevented, the Senior must be the guy who suffers enough through pains of bad code and use that experience as the reason for every Junior’s question : why is it bad ? Below are some reasons that may help to explain to people why the code is bad.

Before naming symptoms, let remind the essence of Coding. Coding, as its heart, in any language, is about defining states and changing defined states. For example in Java, states are varibles, and methods are to update those varibles, and a collection of varbiles & related methods forms a Class in Java. Or in Javascript, there are Functions and varibles and frameworks are actually about how Functions & varibles are organized and wired together. Similarly, databases are to store states permanently and APIs that every programmers know what it is, is about changing states stored in databases. Design patterns are actually about how to define states and how to wire logic – changing states, properly for a single purpose : make it easy to understand. And bad things happen when you don’t know how to wire things properly.

Below is some symptoms that I’ve seen from my works :

Decentralized logic : Logic is about changing states. If a 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, a API, etc, depends on scope. Most of time, we want the logic atomic – means it shouldn’t fails partially. And to make a logic atomic, every pieces of code related to it, should be located in the same places, closed together, so that programmers always have a quick understand on how states will be updated. Gathering codes closely, means has easier & more intuitive roll back strategy. If pieces of code are splitted into multiple components, there are many chances that it will fail partially, and a logic that fails partially causes bugs.

Decentralized logic also make programmers spend more time on finding and gathering information about states and how states are updated. If your programmers always have to search usages of a particular varible or method around the project before dare to make some update on a particular feature or business logic, there is a chance that feature or logic is decentralized. Spend time to centralize them.

Out of pattern : The reason why development teams always want to use a particular framework is not only about reducing development time but also because frameworks contain proven patterns that help the source code clean and clear, easy to read and scale. Beside strategically patterns defined by the framework, every team also define their own tactical patterns like naming conventions, module dividing rules, etc. Every patterns bring its own some reusable components and pre-wired properly so that it make sure a small change only need small number lines of code, small amount of time. Out of pattern usually happens when a programmer does not know well about those pre-defined rules. So, if you find out a logic that unfamiliar with existing ones, or there are many defining & wiring components efforts, there is a chance it is out of pattern. But, sometime current patterns can’t solve upcoming problems, defining a new one is ok, but this rarely happens.

Bad Naming: Engineer is bad at naming and most of time, bad naming makes other engineers confused. Have you ever find out a Class contains a run() and an execute(), and a process() method? Or have you find out varibles like tmp_1, tmp_2, or entity1, entity2, etc. Do you have any idea what they are about ?

A varible name should describes its purpose itself so that you can tell other programmers what you intend to do with it without reading the whole functions, understand this or that algorithm to just know what it is. Bad naming usually comes from improperly component defining. We have a rule Single Responsibility in programming. And when a component holds more than 1 responsibilities, it becomes hard to naming. Studying proven design patterns will help you get some ideas on how to divide responsibilities into components.

Repeat someone’s work: Actually this happens because the poor communication between team members so people don’t know what others do. The cost of this is likely you are paying twice for one tool and when the tool need to be fixed, it costs twice too. There is a rule name “Don’t repeat yourself” in programing, but actually, it must be “Don’t repeat ourself”.

Cumbersome solution: This is about problem solving skill of individuals. Some solves it in tidy way, some makes it chaos. But it must be tidy. In pure algorithms like sorting, searching, etc, cumbersome solution maybe the tradeoff for optimizing in speed or memory. But nowadays, those algorithms usually are packaged into libraries. Most of time we deal with business logic and if that business logic does not have to deal with memory optimizing or speed enhancing, it should be simple. Clean code is over Clever code. The symptom of a cumbersome solution can begin from bad naming in varibles and methods, too much temporary things or a bunch of loops and conditions compacted into one place.

Smell of the Hell : The term Hell in programming means “Overusing”. Overusing in anything is bad, right ! . We may have heard about terms like “Callback hell” in Javascript world and “Inheritance Hell” in Java world, let find out more about those hells by googling it.

The cause of overusing things is people don’t have proper understanding about what they are using. If you are writing Javascript, and you find out you have to chain more than 2 callbacks, it is time to find other approach, like using Promise or async/await function. If you are writing Java, and you find out a Class that extends from others but have to override too much non-abstract methods, there is something not clearly in your class hierarchy. There is another rule when dealing with Inheritance is “Composition is over Inheritance”. Complex component is compacted from simpler components, not from a complex hierarchy.

Hard to document or express in paradigm: Try to express the component and how components wired together to 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.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s