< Back

Maintainable Code, Why And How?


Posted by Binh Pham on 25 Dec 2019 at 08:44

For software engineers, writing code is not a problem but writing maintainable and testable code is a difficult task. This requires a whole lot of time and effort. However, with maintainable code, the development process will be much easier in the later stages. No developer wants code that is difficult to work with or impossible to change. 


There are a lot of controversy on the best architecture pattern to use in software development, whether it’s MVC (Model-View-Controller), MVP (Model-View-Presenter) or MVVM (Model-View-ViewModel). Recently, there appears to be some criticism around the MVC pattern. 


Following are the 2 rules that the East Agile team applied on our projects to keep the code maintainable and testable. 


1/ Make Sure You Write Clean Code

The first fundamental rule of producing maintainable code is writing clean code.


According to Bjarne Stroutsrup, inventor of C++, clean code should be “elegant and efficient. The logic should be straightforward to make it hard for bugs to hide, the dependencies minimal to ease maintenance, error handling complete according to an articulated strategy, and performance close to optimal so as not to tempt people to make the code messy with unprincipled optimizations. Clean code does one thing well.


Starting with clean code helps to generate highly modular source code that is easier to read and test. This also makes code reviews easier by establishing a common vocabulary and a clear framework. 


Robert C. Martin’s book: Clean Code: A Handbook of Agile Software Craftsmanship explores the Clean Code principles in detail. If you’re interested in practicing to write clean code, give it a look! 


2/ Write Code That Is Easy To Modify or Extend

Let’s look at the example below:

This is an example of bad/ unmaintainable code. But how can you tell?



If we skip all the initialization code, the remaining part would be quite easy to catch up and the implementation seems all related to sending media message. But how will you test this? Let’s look at the code again and do the math, there are a lot of cases that need covering and the number of cases will increase at a rapid pace. Many developers will just select a few cases among these to test and this makes testing completely meaningless. 


If your code has too many cases to cover, it is probably because your service is overloading. Do not add the code to one class just because it seems related. When you change how total time is calculated while sending analytics, you will also need to make changes to the “send” function which might lead to a change of the correctness of “shouldSendMessage” in the same class. This will increase the chances to pass the test.

  • First, it violated The Single Responsibility Principle

Looking at the implementation again, you will notice that MediaMessageSender is highly coupling to MessageRepository, MediaSendingAnalytics and FileUpload because it is accessing and initializing those services right in the implementation.

  • Second, it violated The Dependency Inversion Principle

In the case above, if we wanted to add a few more logs or if we wanted to sync message’s status on the cloud, we will then need to modify the implementation of MediaMessageSender.

  • Third, it violated The Open/Closed Principle

In software design, there are many different ways to deal with one problem. In this case, we can consider applying a delegate or interceptor pattern to solve the issue. In this example the two patterns are similar, which means we apply the interceptor pattern.

Refactoring code - apply Interceptor
To see how analytics and other parts work after the refactoring, try this:

You can see that all of them can now be tested easily. 


From the example above, we can see that SOLID principles should be an important guideline to help developers produce more maintainable code. 

  • The Single Responsibility Principle states that a module should have only one reason to change.

Note: Be aware of the word “change” and always ask yourself why you should add that code to this? For example: should I make change of MediaMessageSender if the logics of analytics changed? Definitely not.

  • The Open/Closed Principle states that module should be open for extension but closed for modification.

By using and depending on abstractions only, we can change the behaviour of MediaMessageSender by making changes to set of interceptors without touching its code base. That is the beauty of polymorphism. 

  • The Liskov Substitution Principle states that objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

  • The Interface Segregation Principle says many client-specific interfaces are better than one general-purpose interface.

  • The Dependency Inversion Principle: depends on abstractions, not concretions.

The habit of following SOLID principles and The Clean Code architecture will enable developers to improve code quality and will help them understand the most well-designed software. 


Developers are all guilty of writing crappy codes at times. We’re human and we make mistakes. However, by learning the best practices and techniques, you will find that the overall standard of your code will dramatically increase. When you are under pressure to ship or have to adjust the code due to changing requirements for the millionth time, you will have a foundation of best practices to fall back on. 


Questions? Comments? Concerns? Contact us for more information. We’ll quickly get back to you with the information you need.

Leave a comment