Clean Code
By: Avi Dhakal
Tags: Code, Clean Code, Test Driven Development, Professionalism, Programming

Clean Code

Modern society runs on software and or devices, dependant on millions

and millions of lines of codes. A coder basically governs the rules of reality

for these systems which as a fact, still eludes many people.

Programmers as professionals need to adhere to a set of ground rules. What does a developer actually hold dear? If every profession has its set of ethical and philosophical ground rules, why is it not the case for programmers?

You are not done when it runs, rather only when its clean. Code’s clean when it does exactly what it is expected/supposed to do. No surprises.

Clean code is also polite to the reader as it allows the reader to exit early.

Here are a few ways, with which code consistency and the cleanliness of the code can be maintained.

SOLID principles

Single-responsibility principle

Open-closed principle

Liskov’s substitution principle

Interface segregation principle

Dependency inversion principle

These are some of the first things we learn as programmers, if we do follow them is another lengthy discussion though.

Functions

A function should do one thing and you should not be able to extract

another function from it. Functions should not be large enough to hold nested

structures. The indent level of a function should not be greater than one or

two.

Try avoiding switch statements as much as possible. Polymorphism is a suitable alternative. A module should be open for extension but closed for modifications. Switch statements are dependency magnets, you and the future maintainer will thank you for avoiding those.

There should be no side-effects to the code; a change which causes a change in the state of the system.

Garbage collection makes it easier to write safe code. But you shouldn’t be

depending on it as your saviour.

Logical inconsistency; calling a function before its dependency function should be mitigated from the get go, rather than coming back to it during debugging.

Command & Query separation; commands change state of the system, hence return void; anything that returns a value doesn’t change the state of the

system. These two, command and query, need to be differentiated from the get go.

Duplication of code is sloppy, rather make a function and call that.

Structured Programming

Treat software, code as science, rather than mathematics. Tests only prove a

software to be not incorrect, that a software is not failing.

Comments

Comments can be used to explain code, which can not explain itself. Comments can be good, explaining the code perfectly. At the same time, comments can obscure the functionality of the code to a reader. But comments rot faster than the code. The moment you start modifying the code or someone else does, and doesn’t pay attention to doing the

same for the comments, they have the tendency becoming more of a nuisance.

At some point comments say something while the code does something entirely different. And that needs to be prevented as much as possible.

Don’t use JavaDocs for non-API code. Don’t prettify comments as it needs to be at its most readable form in the code. Don’t use non-local references in

comments. Don’t talk about code that is somewhere else.

Naming Conventions

A variable name’s length should be proportional to the size of the scope that contains it. The name of a function should be inversely proportional to the size of the scope that contains it. As the scope of a function increases, the function gets called more frequently. Name of the classes, same as the functions. As the scope shrinks, the name grows. A good system of names not only explains the working of the function or the class, rather also the environment or the system as a whole. Distinguish the names meaningfully, ambiguity in the names of the functions result in ambiguous code.

Code Reviews

Always go through the code, reviewing the code along with the reasoning the author implemented the code as it is.

Professionalism

The number of programmers has been increasing exponentially and as long as the progression remains, long as we follow the same rate, the industry will be in the state of perpetual inexperience.

Every programmer has their own ways of keeping the code as functioning as presentable as possible. Here are a few ways to further them.

-> Do not ship shit.

-> Always be ready.

-> Focus on stable Productivity (of the Group)

-> Inexpensive Adaptability (of the System)

-> Fearless Competence : always leave the code a little better than you found it

-> Extreme Quality : Unit tests for each line of code you write; Code Coverage

should not be limited as a management metric, let it be a Team metric;

QA shouldn’t be the tool to find bugs in a system, finding problems in a

system should be the work of developers, not QA

-> Automation : if a computer can do it, a computer should do it.

-> We cover for each other. : work as a Team

-> Honest Estimates : rather than a single estimate, give estimates based on the scenarios, on how the run of the project might be, either ideal, best-case,

worst-case;

-> You say no. : Deal with pressure; Don’t accept things just because it comes expected

Sequence, Selection & Iteration

Software Technology went through a rapid discovery period. Most of the

recent/current tech are either improvements or rehash of the same old tech. The fundamental invariance of software development are sequence, selection and iteration. The fundamental code hasn’t changed that much. A change in paradigm would be a consolidation to a language that is functional, object-oriented and structured at the same time. And that still is ways to go.

Test Driven Development

Test driven development is a discipline and part of this discipline is

arbitrary.

TDD can be maintained with these guidelines as the basics :

-> Don’t write a line of production code, until a test case for it is written.

-> Don’t write more of a test than is sufficient to fail.

-> Don’t write more of production code than is sufficient to pass the currently

failing tests.

-> Tests and production code move in the opposite direction.

-> Don’t go for the central behaviour early, don’t go for the gold.

-> Tests have to be decoupled from each other.

They have to be the low-level documentation for the system. A test suite that

doesn’t allow you to make decisions when it passes is a useless one. GUI are

hard to test as you don’t have an expected output. Move the intelligence out of

the GUI and test the intelligence, as the GUI itself has less points of failure

than the GUI with holes in its intelligence.

TDD is double entry bookkeeping.

Write tests that forces you to write the code, you already know you want to

write. Tests should gear towards being more specific and production code more towards being general. Write unit tests and refactor the production code. Do not couple unit tests to production code, which leads to the fragile test problem.

Fundamental strategies for TDD : Outside-In (Marks & Test-doubles) & State-full (relies on more the return values of functions)

Exploratory Testing : Taking a system and trying to break it is a good method but a very few can do it in a stable and documentable pace. What good would exploratory testing do, if rather than debugging an issue, your time would be lost in trying to replicate it?

Software Architecture

Software Design & Architecture are basically the same thing. Minimize the

manpower required to build and maintain software systems. The secret to going fast is not to make roadblocks that may slow you down in the long run. Messes aren’t faster even in the short term.

Software has two values : of what it does & its structure.

A non flexible software can’t change itself as the requirements change, which is going to decrease its value as well as cause more issues during modification.

Behaviour is urgent, architecture is important.

Clean Architecture

A system’s architecture at the top most level should always make the

intent of the app clear. Boundaries are the essence of the architecture. A

database should be in the other side of the architectural boundary. A good

architecture allows you to deffer critical decisions till a later date. It

maximizes the number of decisions not made.

Commitment to a framework is an asymmetric relationship. You commit to the framework, and it can make the first few months of the development cycle

easier. But coupling with a framework hinders your developmental pace in the long run, as new scenarios come forward which the framework never had anticipated.

It is better and cost effective to build something than to buy, even when the

object is free.

Project Management & Agile

Mismanagement occurs because of incompatibility between the priorities of

management and the developers. For a successful completion, a project has to

take careful considerations of the trade-offs a successful completion incurs.

Requirements change often because customers don’t actually know what they want,

until they see something they don’t really want. Hence, early deployment or

keeping the customer in the development cycle leads to a successful project

completion.

Analysis and design aren’t boolean deliverable. They evolve as the project

continues.

You can go much faster producing crap.

Estimates get better as you start reducing the variables. Agile is to get the

bad news out as fast as possible. Agile is for doing small things in small

teams in small cycles.

-> Acceptance tests are prepared by QA and the stories are finished only when

the all acceptance tests are successfully executed by the developers.

-> Non-functional architectural or design requirements don’t need to be

mentioned to the stakeholders, unless they have to be immediately worked on.

-> Case studies show the pragmatic application of the rules.

In the end, this is an engineering. There will always be trade-offs. Which ones

are optimum are for the developers to decide. And please, do decide better, do it yourself, than getting it decided by someone else, which has never been a good experience.