About 5 years ago, I wrote a post titled “10% Development, 90% Maintenance“. That was my view on software development and still is today. Was frustrated by some code review recently and was thinking of how to communicate my penchant for maintainable code simply to both developers and non-developers. Came up with 3Cs for coding – Consistency, Context, Continuity.
1ST C – CONSISTENCY. This focuses mainly on coding style, standards and output. If your code is consistent in the way it is written, its directory structure, the naming of the variables/functions, its behaviour/output and etc., it makes life easier for others and wastes less time for everyone (including yourself). Examples of official coding standards for programming languages are PSR-12 for PHP and PEP 8 for Python.
Consistency reduces cognitive friction. Imagine trying to read thru a newspaper article with a lot of spelling/grammatical mistakes, missing spaces and a mix of British/American/Australian English. You will probably be too hung up by the errors to focus on the article itself.
// sample MESSY Code
function test(id){
if ( id == 1)
console.log('One');
if (2 === id ) {
console.log("TWO");
}
}
Consistency reduces time-consuming guesswork. If you are consistently naming your variables using camelCase convention and I need to search for variables related to a person’s ID, I would just need to search for “personId”, and not variations like “person_id”, “PERSON_ID”, “personID”, “Person-ID”, or even waste time trying to come up with a Regular Expression pattern like /person[-_]?id/i. And this is just for searching – replacing would be another beast. For the sample API response below in JSON format, how shall a new key related to a person’s ID be named?
{
"snake_case": 1,
"camelCase": 2,
"PascalCase": 3,
"kebab-case-sounds-tasty": 4,
"CAPS": 5,
"sUpErCaLiFrAgIlIsTiCeXpIaLiDoCiOuS": 6
}
Consistency reduces unwanted surprises. Imagine manually typing/copying/pasting/running 10+ commands in an exact sequence each time a Docker image needs to be built/pushed, each time an application needs to be deployed, each time you need to SSH into a bastion host by supplying a MFA code to an AWS SSM command so as to connect to an AWS RDS database, etc. The possibility of mistakes and different errors/results would be very high. Automation (e.g. use of GitHub Actions for CI/CD) and provision of user-friendly scripts (e.g. shell scripts, Composer/NPM/Poetry scripts) would be useful in ensuring consistent results each time.
2ND C – CONTEXT. Context gives meaning to code and helps others understand the rationale behind. Woe to the newly hired developer who deletes a line of code cos it seems unnecessary but it turns out that the deleted line affects some obscure logic spread across 10 other files in 5 different folders π
Context can be preserved via docblocks. These are specially formatted comments that use annotations to document specific segments of code, typically variables, functions and classes. Examples of docblock standards would be JSDoc for JavaScript, phpDocumentor for PHP and apiDoc for documenting REST APIs.
function z(q, r) {
// Imagine reading 1000 lines to try to understand what the method does,
// what it takes in for input and what output it returns
}
/**
* Compute height based on aspect ratio
*
* @param {float} width - Width in pixels.
* @param {float} aspectRatio - If 16:9, this value will be 0.5625 (9 / 16).
* @returns {float} Corresponding height.
*/
function computeHeight(width, aspectRatio) {
// Imagine it's still 1000 lines but you can skip reading cos of the
// docblock above :)
}
Context can be preserved by comments. Unlike docblocks that only precede variables/functions/classes, comments can be sprinkled anywhere. Comments can be used to explain the logic behind a for loop, mention that a piece of code is linked to a GitHub issue, add a todo task, document a bug, etc.
document.querySelector('video').addEventListener(
'webkitfullscreenchange',
function (event) {
// GitHub issue #123: webkitendfullscreen doesn't work in macOS Safari
// Must add listener on video element itself, not the document
console.log(event);
}
);
Context can be preserved by documentation. By documentation, I mean plain text files written in Markdown format that are committed with the source code in the same repository. Word documents, PDFs, GitHub wikis and Google Drive documents do not count cos these may need installation of free/paid software/accounts to open, the links/access may be lost when the person who created the documents leaves the company, and are stored separately from the code (what happens to the wikis if the company moves from Bitbucket to GitHub?). In contrast, the Markdown files follow the source code wherever it goes. A simple example would be the standard README.md and CHANGELOG.md in open-source projects.
# Sample README for a project This is stored as `README.md` in the root of the source code repository. Paths mentioned here are relative to the root of the repository. ## Installation - Clone this repo. - Run `npm install`. ## Deployment - The code is deployed using GitHub Actions. See `.github/workflows` folder. ## Workflow - This section explains the customer journey and the architecture.
3RD C – CONTINUITY. Continuity aims to make handovers smoother, easier and complete. This largely involves the imparting of institutional knowledge. Code that has a poorly designed architecture or an overly complicated/convulated workflow, will make the handover difficult, that is if there is even an handover. When was the last time you saw an iPad shipped with an instruction manual? Remember the KISS principle – “Keep It Simple, Stupid”.
Continuity involves increasing the bus factor. Bus factor refers to the minimum no. of developers working on the project that will get knocked down by a bus before the project comes to a complete halt. If the project has a bus factor of 1, all the domain knowledge is stored in a single developer’s brain – if he leaves, no one will be able to continue the project. This extends to troubleshooting as well, with a special mention on the use of frameworks where things automagically work due to Convention over Configuration, making it hard to trace problems when they arise, especially for developers who just use them without understanding how they work (which is why I favoured Zend Framework a lot, now Laminas Project, as its approach is Configuration over Convention, similar to the “Explicit is better than implicit” aphorism in PEP 20 β The Zen of Python).
Continuity involves understanding of human resources (HR). Unlike our grandparents’ era, employees nowadays seldom work for a company for life, i.e. decades, and that is if the company even lasts that long. A developer may not be assigned to a project for life either. Simply put, not many developers will have the luxury of walking down a corridor to ask advice from an elderly developer on a piece of code that he wrote 34 years ago, or inheriting mum’s COBOL codebase π
Continuity involves professionalism. This cuts across all trades, from the road sweeper who diligently covers all his assigned areas rain or shine, to the hawker who cooks every plate of food to exact standards, to the front desk manager who continues to serve the customer with a smile even after getting a tight slap from the customer for no reason. You are paid by your company to do your job. Even if you have your own company, you are being paid by your clients. It behoves you therefore to do your level best despite your emotions and ensure that your code can be easily maintained even after you have left the company.
That covers all the 3Cs – Consistency, Context, Continuity. And frankly speaking, a common “D” is needed to accomplish them – Discipline. But, that’s another topic for another day, ad huc π
[UPDATE 28 FEB 2022]
Additional Readings:
- Discipline Makes Strong Developers: “Discipline. Discipline! I repeat it because the mere presence of a great source control system doesn’t obligate anyone to use it in a structured, rational way. No. That takes discipline.”
- Optimize for Simplicity First: “If it’s slow but readable, I can make it fast. If it’s broken but readable, I can make it work. If it’s impossible to understand, then I have to spend hours trying to understand what the abomination is supposed to do in the first place.”
- Don’t Be Clever: Instead of clever code like
return i > 0 ? i << 2 : ~(i << 2) + 1;, write clear code likereturn Math.abs(i * 4);. - Martin Fowler: "Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
- Brian Kernighan: "Everyone knows that debugging is twice as hard as writing a program in the first place."
- Why Good Programmers Are Lazy and Dumb
[UPDATE 18 JUN 2026]
On why I do not use AI tools when coding:
- Consistency: As the article ChatGPT Isn't Just Changing How We Work. Itβs Harming How We Think points out, "across professions, AI is reducing the opportunities individuals have to exercise the very skills they require when the technology fails. The result is a loss of judgment in unfamiliar situations and a reduced capacity to intervene when AI systems produce flawed or misleading outputs". I'm more of a hands-on person - I need to continually code by hand to improve or maintain my ability. The brain is like a muscle (figuratively) - use it or lose it, and the neural pathways formed when learning new skills get reinforced with practice, making the circuit fire more quickly and accurately the next time, i.e. practice makes perfect.
- Context: As per the article AI Made Writing Code Easier. It Made Being an Engineer Harder, "when you write code, you carry the context of every decision in your head. You know why you chose this data structure, why you handled this edge case, why you structured the module this way. The code is an expression of your thinking, and reviewing it later is straightforward because the reasoning is already stored in your memory". I form a mental model when I code by hand, which helps immensely even when I review the code 5 years later. From Chapter 2 page 15 of "The Mythical Man-Month" by Fred Brooks which elaborates on the concept of "idea, implementation, interaction" in "The Mind of the Maker" (by Dorothy Sayers): "For the human makers of things, the incompleteness and inconsistencies of our ideas become clear only during implementation. Thus it is that writing, experimentation, 'working out' are essential disciplines for the theoretician". Hence, I would infer that writing code by hand is different from writing prompts/specifications for AI agents, the latter still being in the idea stage for us who will never discover nor understand the imperfections of our ideas if we rely on it.
- Continuity: As noted in the article Why Generative AI Coding Tools and Agents Do Not Work For Me, "the problem is that I'm going to be responsible for that code, so I cannot blindly add it to my project and hope for the best. I could only incorporate AI generated code into a project of mine after I thoroughly review it and make sure I understand it well. I have to feel confident that I can modify or extend this piece of code in the future, or else I cannot use it". 5 years down the road, at 3am in the morning, on an on-premises server, when the Internet is down, when none of today's AI tools exist anymore, can I tell the client that I can't solve the problem because the code was written by an AI agent and I do not understand the code fully?
[UPDATE 12 JUL 2026]
On choosing apiDoc over Swagger/OpenAPI:
-
Consistency: Programmers generally do not like to do documentation. Looking at the following examples for a single API endpoint, which do you think programmers would find easier to do and actually do it, for 1000 API endpoints? Remember, we can't talk about consistency if we don't even do it π
Example of apiDoc docblock in
src/api/modules/pet/routes.jsof project (where the code for creating the pet is):/** * @api {post} /api/pet/add Add Pet * @apiName AddPet * @apiGroup Pet * @apiDescription Add a new pet to the store. * * @apiBody {string} name Name of pet. * @apiBody {string[]} photoUrls List of URLs of photos for pet. * @apiBody {string="available","pending","sold"} status Status of pet in store. * @apiExample {curl} Example usage: * curl --location --request POST "http://localhost:10000/api/pet/add" * --header "Content-Type: application/json" * --data-raw '{"name":"doggie","photoUrls":["https://images.example.com/doggie.jpg"],"status":"available"}' * * @apiSuccess {number} id Record ID. * @apiSuccess {string} name Name of pet. * @apiSuccess {string[]} photoUrls List of URLs of photos for pet. * @apiSuccess {string="available","pending","sold"} status Status of pet in store. * @apiSuccessExample {application/json} Success Response: * HTTP/1.1 200 OK * { * "id": 1, * "name": "doggie", * "photoUrls": ["https://images.example.com/doggie.jpg"], * "status": "available" * } * * @apiErrorExample {application/json} Error (invalid input): * HTTP/1.1 400 Bad Request * { * "error": "Invalid status" * } */ require('express').Router().post('/api/pet/add', (request, response, next) => { require('./controllers/pet.controller.js').add(request, response, next); // create pet });Example of OpenAPI/Swagger documentation in
openapi.yamllocated in root of project (where's the code that creates the pet?)openapi: 3.0.4 info: title: Swagger Petstore - OpenAPI 3.0 description: This is a sample Pet Store Server based on the OpenAPI 3.0 specification. version: 1.0.27 paths: /pet: post: tags: - pet summary: Add a new pet to the store. description: Add a new pet to the store. operationId: addPet requestBody: description: Create a new pet in the store content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' application/x-www-form-urlencoded: schema: $ref: '#/components/schemas/Pet' required: true responses: '200': description: Successful operation content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' '400': description: Invalid input default: description: Unexpected error components: schemas: Pet: required: - name - photoUrls type: object properties: id: type: integer format: int64 example: 10 name: type: string example: doggie photoUrls: type: array xml: wrapped: true items: type: string xml: name: photoUrl status: type: string description: pet status in the store enum: - available - pending - sold xml: name: pet requestBodies: Pet: description: Pet object that needs to be added to the store content: application/json: schema: $ref: '#/components/schemas/Pet' application/xml: schema: $ref: '#/components/schemas/Pet' - Context: As seen in the examples above, the Swagger/OpenAPI documentation for all the API endpoints is usually stored as a YAML or JSON file in the root or a subfolder of the code repository, whereas docblocks with apiDoc annotations are usually written in the source code, just above the corresponding route handlers for each of the API endpoints, the latter of which makes it easier to track and update due to immediacy of context.
- Continuity: If the Internet is down, if the websites for the apiDoc/OpenAPI specifications are not accessible, if the tools for generating nicely formatted Markdown/HTML from the documentation are no longer maintained/available, which would be easier to handover to a new developer? Looking at the examples above, the apiDoc documentation would be easier to read, comprehend and maintain π

