Why Code Quality Is Important and How to Explain It to Your Colleagues

V Kolobkov
6 min readFeb 12, 2021
Glass of water

Code quality in software development— what is this? I’ve met different opinions, and different measures to check it. Quite often it is not considered at all, as long as everything works. Some developers perfectly know their programming languages (much better than I do mine) but care only about proper usage of language features, not thinking about how their code operates globally. Well, what is a “quality” code, and is it really something worth caring about?

Imagine a fictional surrealistic example. Your team works in a room. And on the floor in the center of the room, there always is a glass of water. The law of your Universe is: until this glass is in place and filled with water, your application works. Normally floor is the last place one will expect a glass of water to be, but as far as your team is accustomed to it, everyone is fine. Of course, the duty of pouring water into the glass cannot be left to chance, but being wise and savvy enough, your team established a daily schedule for it and explained the situation in internal docs. Well, every time one walks across the room, he needs to watch out where he steps (don’t overturn the glass!). But it is a matter of habit, and also you have automated tests checking that the glass is alright. When a new teammate sees this glass for the first time, he just takes the glass and puts it on the table (just a natural instinct). But the team knows the outcome, so one of you quickly and gently put the glass back on the floor (yeah, that’s years of practice!), and then kindly explain the rules of this place: first — always keep the glass where it is, and second — whatever happens, keep the glass where it is!

Sounds strange? Yes, a lot! But I’m sure that everybody has seen such “glassy” places in the code. The “magic” constants that must not be touched (and it’s good if there is a comment noticing that). The modules with overcomplicated API (that usually means some caveats). When next time you see it in your app’s code, think of it as one more glass of water on the floor of your room. One, two, three… When one of them gets occasionally overturned — is just a matter of time.

Now let’s imagine a more realistic case. Our application may receive a specific sort of data. When it happens, we need to validate incoming data, and if it is correct, then parse it. To handle this case, we create the class ReceiveScpecificData:

// JavaScript
class ReceiveSpecificData {
constructor () {
this.data = '';
}
receive(data) {
this.data = data;
retutn this;
}
validate() {
if (!this.data || this.data.length === 0)
throw new Error("Incorrect input");
return this;
}
parse() {
return JSON.parse(this.data);
}
}
const receiver= new ReceiveSpecificData();const parsedData = receiver.receive(data).validate().parse();

At a glance, it looks good (and I personally like chainable methods). But think of that: we always must apply all these methods, in the given order. Once you forget to call validate() method, inconsistent data may crash your app, i.e. the glass gets overturned. I.e. it is not evident, it’s just something to remember.

One way to simplify it is to bring all these operations inside the ReceiveSpecificData class, and apply them internally. Something like that:

// JavaScript
class ReceiveSpecificData {
constructor (data) {
this.data = data;
}

processData() {
this._validateData();
return JSON.parse(this.data);
}
_validateData() {
if (!this.data || this.data.length === 0)
throw new Error("Incorrect input");
}
}
const receiver = new ReceiveSpecificData(data);
const parsedData = receiver.processData();

A bit more stable. Or even better — there’s no reason to create an instance of ReceiveSpecificData class. We can make processData() method static:

// JavaScript
class ReceiveSpecificData {
static processData(data) {
ReceiveSpecificData._validateData(data);
return JSON.parse(this.data);
}
static _validateData(data) {
if (!data || data.length === 0)
throw new Error("Incorrect input");
}
}
const parsedData = ParseSpecificData.processData(data);

Now, the implementation details are not exposed outside the class, and fewer things to keep in mind when using it.

Now a real life example. Last few years, I’ve been working in web development. When I need to save something locally in the user’s browser between their sessions, I use localStorage object available in the standard browser API:

// JavaScript
// Save value in storage:
window.localStorage.setItem(“some-key”, “Some value to store”);
// And now get the value back:
const value = window.localStorage.setItem(“some-key”);

Simple and plain! But recently, I’ve started learning how to develop apps for Android, and was pretty surprised how a pretty similar goal is achieved there:

// Java
// Get SharedPreferences instance to work with storage
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
// Save value:
// 1. get Editor object
SharedPreferences.Editor editor = sharedPref.edit();
// 2. save value in storage with Editor
editor.putInt(
getString(R.string.saved_high_score_key),
newHighScore
);
editor.apply();
// Get saved value:
int someIntValue =
getResources().getInteger(R.integer.saved_high_score_key);

Yes, SharedPreferences mentioned here was deprecated in API level 29, I know. But this is just an example, and it is still met in real applications. So, to read a value from storage, first, you need to create a SharedPreferences instance. To save the value, you also need to incept a SharedPreferences.Editor instance out of it and give it a use. If you need only to save the value, you create two objects to achieve this, and one of them — just to create another one.

I’m very new to Android (and not very talented, actually), so guess there is a strong reason for these things to be done as they are. Most likely that’s for sake of optimization, which is more crucial for a mobile device with 1 GB RAM onboard, than for desktop browsers that are notorious to be OK with “eating” 1 GB RAM just for a single tab. But still, comparing all these preparations with something taking two lines of code in a browser was a little bit shocking for me (literally, I realized that the world is more complex than I used to think). So, if I saw a library introducing a similar interface in web development, I would think of it as something that could be simplified a little.

Usually making the implementation simpler (and thus cleaner) is just a matter of a small refactoring, but it is often omitted. The common thinking is: “Why to change if it works and we know how it works? And if somebody doesn’t know, they can look into the source. And so we’ll do when we get back to that code 6 months later”. But keeping things simple doesn’t mean being too lazy to investigate how it was done — it is more about saving your time and mind for something more important and less routine, over and over again. And also that saves some money for your boss when next time you accomplish a task in 20 mins, instead of 30. Paying 30 mins now, you may “buy” your team several hours in the future. And yes, people come and go, so what is pretty clear for us now, may come not that easy for somebody who joins our team later, in place of the guy that implemented this part of the functionality.

There is no ultimately perfect application existing in the world, that’s true. But the fewer fragile places its code contains — the more stable and reliable the app is (and fewer chances your boss urgently calls on you early Sunday morning).

If your team already appreciates the things to be done properly — congratulations, and keep it up! If not — there are two options:

  1. Cross your fingers everybody and hope everything will be good until your app is retired one day, and you start a new one (and all repeats again). Keeping your app’s life cycle shorter than average time to seriously fail is a possible approach too.
  2. Start changing the things — gently, step by step. If your colleagues don’t share your opinion, you may be misunderstood (i.e. looked at like an idiot), so be careful with it (I warned!). Don’t rush into it. Be kind, patient, and, again, careful — that’s a long thorny road. How to propagate changes in a team — is not an easy question, and it’s more about communications and leadership, so be ready to improve your skills there too.

Anyways, clean code — it is a long endless journey, rather than a certain destination. Wherever you go, be lucky and safe.

And thanks for reading till here.

Further reading:

  • Robert Martin “Clean Code”
  • Steeve McConnel “Code Complete”

📷 Photo on the preview: Stephan Müller

--

--