Writing Clean Code: How It Impacts the Future of Your Product

Georges Lteif

Georges Lteif

Software Engineer

Last Updated on May 19, 2022.
Subscribe now to stay posted!
About Us
13 min read

1. Overview

Every software developer would agree that writing clean code is vital for the team, product, and business.

The question then becomes, why is poor code so abundant, so that quality code is the exception rather than the rule? A follow-up to that question would be: can we efficiently identify bad code and do something about it?

The first question (origins of bad code) has many answers: legacy code, lack of talent and skills, inconsistent coding styles, poor code production processes, insufficient design or refactoring efforts, the list goes on.

Quality vs Effort
Quality vs Effort

The answer to the second question is a bit more subtle. The following reasons come to mind:

  • In general, software quality problems can never be totally eliminated. The Pareto principle tells us that the first 80% would require 20% effort, while the remaining 20% would require perhaps an eternity to solve. In other words, to increase the quality by a certain amount, the effort required increases exponentially rather than linearly, which is not how we want it to be.
  • Projects are contained by time, budget, and the necessity of producing business value. Quality is the first sacrifice made when you run out of time or money. What you are left with is called technical debt, a concept that we will explore a bit further. Technical debt can be reduced with constant refactoring, another important topic we will dig into.

Fixing bad code is risky and requires plenty of effort. Too much lousy code, however, can ruin the product (and the business), as we shall endeavour to describe in the coming sections.

So, where exactly do we draw the line on quality and are there any simple actions we can take to keep low-quality code under control?

This article will attempt to answer these questions in some detail. The central ideas will be as follows:

  • Properties that define clean code include readability, maintainability, and sound design
  • Processes you can implement to keep your code up-to-scratch
  • Long term implications of poor code on the business.

In the context of this discussion and before we proceed, we will make a distinction between “quality code” and “clean code”.

Clean code refers to code that doesn’t stink or, more specifically, has a consistent style, is accessible, and does not contain any fundamental flaws such as strong coupling of independent features or poor abstraction.

Quality code takes clean code to the next level; it is also easy to maintain, extend and test. These three metrics: productivity, extensibility, and testability are what we shall use to assess code quality.


2. Table of Contents


2. State of Software Development

Software development has changed a lot since it kicked off in the 1950s, and it has expanded exponentially and in every direction.

Challenges of Developing Quality Software
Challenges of Developing Quality Software

This expansion has been so fast that coding standards and best practices have trouble keeping up.

Bob Martin recounts an interesting story in many of his talks. It goes like this: The number of software developers worldwide doubles every five years, so looking at a random sample of software developers, chances are that half the population will have less than five years of experience in development.

This fact is remarkable as five years is just enough time for software developers to acquire the necessary skills, especially since software developers will also move up the corporate ladder sooner or later. These two facts make it harder to find experienced developers.

Under these conditions, users today are happy to have applications that run just about right instead of perfectly all right.


3. Quality Code

3.1 Assessing Code Quality

In this section, we will provide our views on assessing code quality.

Software Code Quality Assessments
Code Quality Assessments

We will look at three different dimensions: 

  1. Productivity or how easy it is to work with the current code (update, improve, and maintain).
  2. Extensibility or how easy it is to extend its functionality.
  3. Testability or how easy it is to test the product’s features, especially through test automation frameworks.

3.2 Productivity

Two factors primarily impact productivity in software development: readability and accessibility.

Productivity as Readability and Accessibility
Productivity as Readability and Accessibility

3.2.1 Readability


Code is like humour. When you have to explain it, it’s bad.

Cory House

Readability measures how easy it is for an experienced developer to read and understand a piece of code. One compelling factor in readability is code style.

Code style refers to conventions that developers follow when writing code. It consists of rules and guidelines on naming, the use of certain features of the coding language (such as macros and define statements), commenting, where to place braces, how to use the source code versioning tools, and the usage of white space.

Rules governing code styling can be exhaustive and are usually a great asset to developers.

Other factors that dramatically enhance code readability are some features of source code versioning tools such as historical search and blame.

We assume that every software team uses git or some other tool (if not, they should!).

The blame feature, for example, allows developers to access the history and context of any change. This feature, available from git with no extra effort, is very powerful as it removes the burden of including historical context details in the code itself, usually through comments.

3.2.2 Accessibility

Accessibility is a measure of how easy it is for an inexperienced developer to understand or modify a piece of code.

We assume that inexperience in this context refers to the present body of code or light development experience in general.

Two factors can impact accessibility: the programming language (or technology stack) and the coding patterns used.

One of the principles of Operational Excellence is using mature and proven technology where experienced developers can be quickly recruited, and online material easily found. Mature technology tends to break less, has more features off the shelf, and is probably supported on multiple platforms.

Of the many mature technologies available today, some are low-level while others are high-level. Low-level programming languages require more expertise and lines of code per feature to achieve the same result.

One of Google’s early engineering decisions is “Python where you can, C++ where you must“, and a very wise one indeed.

Remember that the ultimate objective of developing software is creating business value for your customers. The latter may not care which programming language is used or how fast it runs, as long as it solves their business needs in a cost-efficient manner.

You also want to ensure that you beat your competition in the market. There is very little point in creating a perfect product that never makes it to your customers.

A final word on coding patterns before we move on. There are four categories of coding patterns: creational, structural, behavioural, and concurrency which are typically used for coding. Consistent usage of these patterns makes your code easily read and understood and provides a standard dictionary that developers can use to facilitate their conversations.

3.3 Extensibility

Two elements govern the ease with which software code is extensible: sound architecture and low technical debt.

Extensibility as Sound Architecture and Low Technical Debt
Extensibility as Sound Architecture and Low Technical Debt

3.3.1 Sound Architecture

Software architecture consists, in summary, of all the important and difficult decisions you had made when you were creating your design.

The choice of programming language, database layer (vendor, engine, configuration), deployment method (onsite, cloud), user interface (web, thick client, command-line interface), model (microservices, monolithic, distributed, client-server, standalone), platform (Windows, Linux, Unix) all constitute decisions that are hard to change later on and are considered one part of software architecture.

Development design patterns form the second part of software architecture. These include modularity, redundancy, high availability, scalability and many other similar features that we usually refer to as design considerations.

Sound architecture allows you to extend support for another database engine or OS platform, for example, without touching the business layer.

You can keep your code clean when no changes you make will render the software as a whole less modular or scalable or prevent it from compiling on a different platform.

3.3.2 Low Technical Debt

Technical debt is the amount of work you leave behind when you sacrifice quality for faster delivery.

Sometimes, these sacrifices are justifiable, making technical debt a problem that you need to manage and control rather than eliminate.

Technical debt can be kept under control by refactoring, a process devised by Martin Fowler in 2000 with contributions from notable experts like Kent Beck:


Refactoring is a controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations […] the cumulative effect of each of these transformations is quite significant. By doing them in small steps you reduce the risk of introducing errors. […] which allows you to gradually refactor a system over an extended period of time.
Martin Fowler

A prerequisite to refactoring large pieces of code, in my view, is a suite of test cases that provides you with enough confidence to perform the refactoring exercise.

I believe that technical debt, beyond a certain threshold, would become too expensive, disruptive, and challenging to manage. This is when it turns into legacy code. It is much easier and far less risky to refactor code constantly.

3.4 Testability

Testability is a measure of how easy it is to test new code. 

Many factors can influence testability, but our primary focus is on code-specific ones. If you are interested, for example, in automation, tools, or infrastructure, be sure to check our article on that topic.

Now back to coding issues that can adversely impact the ability to test it:

  1. Bulky and complicated methods that cannot be easily unit tested
  2. Not enough modularity so that components can be isolated and any dependencies accounted for
  3. The new code is buried behind layers of other code and is not accessible externally through APIs, for example. While this is not a fault per se, it makes any new code challenging to test.

These issues are less prominent if you use Test-Driven Development or code with automation in mind. Both of these approaches to testing will push developers to write code that can be easily tested.

Code just developed must be ready to be tested; otherwise, if you leave it till later, chances are it will accumulate and eventually get dropped. Quality code should come with a complete suite of test cases.


4. Long-term Consequences of Bad Code

It is vital to recognize the long-term impact of poor quality code on the product and the business. Equally important is recognizing which factors play a role in such assessment and which do not.

4.1 Critical Factors Impacting Code Quality

At the beginning of this article, we briefly touched upon the nature of quality issues, not just in software but in any other product or service.

It is fair to say that creating a perfect product is not easily attainable, making such an undertaking perhaps highly undesirable. The idea is to know where to stop when perfecting quality or when the product is good enough to be mass-produced.

Technical staff are, sometimes by design, other times by choice, oblivious to business needs; their job is to create a great product, regardless of the price. This “design fault” adversely impacts the delivery capabilities of your organization.

Practical thoughts that might be worth considering:

  1. Be patient when training new staff. Avoid the urge to rewrite their code or take over their tasks when their code is not good enough.
  1. Be creative when it comes to sacrificing quality. If you absolutely have to, let it be in the custom modules instead of core—schedule refactoring exercises to keep technical debt under control.
  1. If you are considering a matter that will require additional effort, base your decision on whether it adds value to your product or not.

Point number 3 should be your guiding principle on all contentious matters. An activity that does not add business value might well be discarded regardless of how attractive it technically might be.

4.2 Expensive to Maintain or Extend

Does it take inordinate time to estimate or implement even the tiniest of changes?

Poor code requires significant familiarity from developers to read, analyse, and maintain. All of these tasks are generally undertaken by expensive professionals making them cost-inefficient.

It is not uncommon to hire additional developers (and people to manage them) when strict deadlines are in place to ensure timely delivery. This extra cost is a direct result of poorly written code.

4.3 Bad Code Is a Long-Term Liability

When technical debt reaches certain levels, risk and disruption from large-scale refactoring become too big to ignore. The cost of such exercises becomes prohibitive.

Past this stage, the product turns from an asset to liability as every project it involves carries a significant risk of being unprofitable.

If you can’t fix it, perhaps you can abandon it? In many situations, that also can be costly, as it might hurt the company’s image. In addition to reputational damage, there might also be contractual obligations to support the product.


5. Guide to Maintaining Quality Code

In this section, we will address two broad topics: first, maintaining quality code, and second, some examples of code styling for keeping your code up to scratch.

5.1 Investing in Design

5.1.1 What it means

  • When software teams are pressed for time, the first sacrifice is design and documentation. But that is not ideal. Regardless of the deadlines, guarantee a minimum level of design and documentation, and these things have a notorious way of biting back!
  • Proper design by experienced architects eliminates the potential of introducing redundant code, unusable features, or unreliable solutions.
  • Design investments become more important when projects are more significant or new changes have a system-wide impact.
  • Investing in design keeps the product foundations rock-solid while preventing quick-and-dirty fixes, short-term hacks, subpar implementations, and unreliable solutions from creeping into the codebase.

5.1.2 How to Do It

  • Invest in a solution design document
  • Follow the 70-30 rule for design/development effort
  • Have the design reviewed by architects and developers for feedback and buy-in
  • Allocate a portion of resources for Research and Development (R&D)
  • Commit to one major upgrade every 1-2 years
  • Encourage constant refactoring of old code

5.2 Diligent Code Review

5.2.1 What it means

  • Code review ranked number 1 in a survey on maintaining quality code.
  • Its main objective is to review code against internally published guidelines and industry best practices.
  • Software teams today are actively using project tracking tools like JIRA. These usually offer built-in process flows that allow the Code Review set up as a step within the ticket lifecycle.
  • Code styling is an important area to review. In essence, a Code Style comprises an extensive set of rules governing the writing style (commenting, naming, indentation, spacing, vertical alignment, indentation, usage of global variables or static functions, and so on). It is essential to have an internally published Code Style to help keep the code consistent across the board.

5.1.2 How to Do It

  • Make sure code review is an integral part of your SDLC
  • Make efficient use of enterprise collaboration tools such as JIRA for implementing proper code review processes
  • Publish code styling rules and guidelines (Google’s style guides include various topics and languages. A great place for inspiration).
  • Train new staff on the product and industry best practices
  • Make sure code reviews happen against a well-described and documented design.

5.3 Testing and Automation

5.3.1 What it means

  • Regression test suites with decent coverage make constant refactoring of old code a practical exercise by eliminating any fears of breaking existing functionality.
  • Test automation compounds the advantage of regression test suites by providing a mechanism for shorter and more reliable feedback.
  • When writing new code, make sure to add the necessary test cases. Do not commit code that does not have unit tests.

5.3.2 How to Do It

5.4 Sound Architecture

5.4.1 What it means

See the previous section on sound architecture to understand its impact on quality code.

5.4.2 How to Do It

  • Choose standard, time-tested architecture and design patterns suitable for your product requirements.
  • Encourage collaboration between architects and developers when designing new functionality or modifying architecture.
  • Use Other People’s Experience (or OPE) when implementing solutions for existing problems.
  • Hire architects with the required skill sets and implement the necessary processes to oversee significant design decisions.
  • Make sure new functionality is in line with the overall architecture of the product.
  • Constant refactoring and investment in R&D help keep the product up-to-date with the latest technological standards.

5.5 Publish a Styling Guide

5.5.1 What it means

  • Publish an internal Styling Guide that everyone can read and follow
  • Make sure its part of the code review process
  • Code Styling should be geared for maximum readability

5.5.2 How to Do It

  • Get inspiration from online content (Google Styling Guide is a great example)
  • Have a look at the below subsections for some practical examples which we found useful

6. Final Words

Producing quality code should never be a spurious exercise left to the staff’s discretion, and it is best viewed as a collective effort enforced through appropriate internal policies and processes.

The quality of a codebase has far-reaching consequences on the business. Bugs are just the tip of the iceberg, while performance issues, huge backlogs, costlier projects, poor customer experience, and a suffering reputation follow shortly.

Software development companies are ever more pressured to produce better products in shorter and shorter timeframes.

This objective, which we call Operational Excellence, can only be achieved by embracing better disciplines allowing for efficient design, quality coding, and better testing.


7. Further Reading

  • An awesome article on modular design can be found here.
  • Great content from Bob Martin on clean code.
  • Another set of great videos that will help you start with design patterns

Technical Risk Management and Decision Analysis — Introduction and Fundamental Principles

1. Overview I could not find a better way to start an article on Risk and Risk Management than by quoting the opening lines of Donald Lessard and Roger Miller’s 2001 paper that, briefly but lucidly, summarizes the nature of large engineering endeavours. It goes like this: This article leans heavily on three handbooks thatContinue reading “Technical Risk Management and Decision Analysis — Introduction and Fundamental Principles”

Complexity and Complex Systems From Life on Earth to the Universe: A Brief Introduction

1. Overview Dealing with complexity is an integral part of our lives, even if we do not realise it.  An organisation can be modelled as a complex system from the scale of megacorporations right down to the smallest teams. The architecture of software solutions can be equally complicated, and megaprojects and implementations are certainly involved.Continue reading “Complexity and Complex Systems From Life on Earth to the Universe: A Brief Introduction”

Book Review: Programming the Universe — A Quantum Computer Scientist Takes on the Cosmos

Synopsis Most physical theories adopt a mechanistic view when examining natural phenomena where any system can be modelled as a machine whose initial conditions and dynamics govern its future behaviour. In this book, Programming the Universe — A Computer Scientist Takes on the Cosmos, Professor Seth Lloyd proposes a radically different approach centred around aContinue reading “Book Review: Programming the Universe — A Quantum Computer Scientist Takes on the Cosmos”

From Abstract Concepts to Tangible Value: Software Architecture in Modern IT Systems

1. Overview Software design and architecture are two very elusive concepts; even Wikipedia’s entries (ref. architecture, design) are somewhat fuzzy and do not clearly distinguish between the two. The Agile manifesto’s statement on architecture and design is especially brief and raises more questions than answers. The most common definition of software architecture is as follows:Continue reading “From Abstract Concepts to Tangible Value: Software Architecture in Modern IT Systems”

Business Requirements and Stakeholder Management: An Essential Guide to Definition and Application in IT Projects

1. Overview The complexity of business requirements in IT projects has experienced exponential growth due to pressures by increasingly sophisticated client preferences, novel technologies, and fierce competition. Consider, for example, the case of financial payments. In the mid-80s, most payment transactions occurred inside bank branches, and only the biggest banks offered services on ATM orContinue reading “Business Requirements and Stakeholder Management: An Essential Guide to Definition and Application in IT Projects”

Loading…

Something went wrong. Please refresh the page and/or try again.

Leave a Reply