Theoretically Speaking — Clean Code & Clean Architecture
This blog aims to highlight the ideology and engineering mindset to be followed in order to come up with and maintain a product with clean code and clean architecture.
- Minimise chaos and maximise stability & agility of the application.
- Ensure a steady speed of development for each sprint, counted by the number of features added. Each new feature addition should require a similar amount of effort.
- Need for refactoring or redoing an application in future should be eliminated.
At this point, the code in each component will be in a state where understanding and modifying the behaviour of the code should be straightforward. Documentation should not be required to explain the structure or logic of the code. Coding conventions and formatting should be standardised for all programming languages used. Any new additions made to the code should not change the state from “clean code” to “almost clean code”.
At this point, the application architecture would be in a state that can handle scale and be stable in each release. A new feature addition may involve adding some pieces in the architecture, but should never result in modifying the existing architecture. Logging should be effective and usable. Monitoring should be realtime & proactive, both for the developers as well as the business team. Productivity of a developer should not be hindered due to limitations in the architecture. Blast radius should be minimised for any component failure.
Cost of feature development and cost per release keeps on increasing if the code and architecture are not clean. Maintainability also becomes a challenge. Adding a new developer doesn’t necessarily mean an increase in the speed of development. As the amount of features increase, the amount of “oh no”s with each new feature request also increases.
Product scope gets impacted due to the increasing estimates with each feature release. In a rapidly growing startup, the product scope should be determined purely based upon business requirements instead of technical challenges. In order to achieve this, a sustainable and scalable engineering process should be established which can ensure the products are developed using clean code and clean architecture.
To understand a single use case within a component, if the programmer has to read and understand the entirety of the code, then it needs to be refactored.
News Article Analogy
While reading a news article, the reader can:
- Just read the title and get a rough idea about the topic
- Continue to read the summary and get some more details
- Continue to read the full article to know each & every detail
- Click on reference links to further dig deeper into the topic
At any point, the reader has the choice to stop at whatever level of detail he/she desires to know, and still have an overall understanding of the topic.
Code should also be structured in a similar manner. Refactoring must be done in order to ensure readability of the code is improved, DRY (Don’t Repeat Yourself) principle is followed. Ideally TDD (Test Driven Development) should be encouraged for new products.
While adding a new feature, the existing structure of the code should be preserved. Independent patches should not be added which deviate from the overall flow of data and design of code. Getting the functionality right should be the first goal, cleaning the code should be the immediate next one.
“We’ll clean the code later” is a lie and should never be trusted.
A PR (Pull Request) should only be approved if the code is clean, and not if it barely does what it was supposed to. Each code review should be conducted by at least two reviewers, one of them who knows the functionality being reviewed & can check for logical aspects of the code and, the other one being the chief architect who can ensure the code is clean and suggest any optimisations if necessary.
If at any point, the current state of the architecture becomes a hurdle in adding a new feature into the application, it’s time to re-architect. The goal of this process is not to optimise the state of the architecture for the current feature additions, but instead to evaluate what went wrong at the core of it and resolving it in a holistic manner in order to ensure such incidents in future are minimised.
To support certain mechanisms or workflows in an application, some pieces might be added into an architecture. However, in this process, the architecture should be evaluated as a whole and not looked upon as “previously clean architecture + new mechanism”. Having a clean architecture takes the highest priority and must not be compromised for an urgent business requirement.
If there’s a situation where we have to remove/modify an existing piece of the architecture in order to accommodate certain functionality, something has gone wrong and the architecture should be re-evaluated. Again, the architecture should be evaluated as a whole and it should not be looked upon as “previously clean architecture - the troubling piece”.
Following the Agile process of development, an application might go through a number of iterations of re-factoring and re-architecting. If the ideologies mentioned above are followed in each iteration, the architecture and code would always remain clean. After a number of iterations, the entire process would become repeatable and hence, scalable & sustainable.