Programs must be written for people to read, and only incidentally for machines to execute.
Structure and Interpretation of Computer Programs
Clean Code & Unecessary Complexity?
The following two short programs implement the same functionality:
# v is defined somewhere above
s = 0
for i in range(len(v)):
s = s + v[i]
# velocities is defined somewhere above
sum(velocities)
Which one is better?
Whichever option you chose, though you likely picked option 2, why? Code fundamentally serves to communicate instructions to machines, so if it does so in a correct manner, why is one program better than another?
Malicious Code
Dirty code, such as the code in the first snippet, is malicious. It taxes readers unnecessarily by forcing them to spend time and energy deciphering its inner logic. To put it another way, it has unecessary complexity.
Code complexity refers to how challenging a piece of code is to understand. It relates to the structural and conceptual intricacies present in the code. Complexity is not inherently bad. By understanding code we mean being able to read it and understand the underlying logic, the responsability each line has, the inputs and outputs and so on.
Necessary complexity can be defined as the minimum required complexity a piece of code must have to still solve the underlying problem for which the code was written. Unnecessary complexity is any complexity beyond this minimum. Note that this concept of necessary complexity goes beyond syntax and includes design, documentation and all aspects related to the user and developer experience.
When you read the first snippet of code:
# v is defined somewhere above
s = 0
for i in range(len(v)):
s = s + v[i]
you likely took a few seconds, maybe more, to understand what the code is doing, what s
would be, why it's first defined as zero and then incremented, what v
might be. You likely then came to the conclusion that the loop iterates over the elements of v
and sums them up, assuming that the + operator is doing arithmetic and not something nasty such as concatenation, and stores the summation in the variable s
which is initialized with the value zero.
This code, has unecessary complexity! It demanded of us more labour than required. The minimum complexity solution, and therefore the clean code one, would be something like:
# velocities is defined somewhere above
sum(velocities)
We can now even see that v
was actually some datastructure storing velocity information on which we compute a sum.
Code is clean when it solves an underlying problem using only necessary complexity.
Clean Code is more than you might think!
To motivate this series of articles, we presented a few short snippets of messy code. It is important to realise that larger pieces of code, are not equivalent to a series of short pieces of code. Larger pieces of code have more complex properties and require thoughtful abstraction and design. These are topics we will discuss in later articles.
Why is Unnecessary Complexity an Issue?
Useful code is rarely a static finished piece of work. Code bases are dynamic, evolving to adapt to ever-shifting requirements and demands. Think of a code base more like a city, rather than a single building. In a city, new buildings are constructed, old ones torn down, maintenance is performed and so on. What are the analogous pieces of work that would require one to read code?
Bug Fixing
Any reasonably large piece of code will have some path of logic which leads to some unexpected consequence, such as an error. As bugs are discovered, contributors will hopefully desire to fix them. Fixing a bug usually requires a good understanding of the underlying programmatic cause. The more complex the code, the more difficult finding the root cause becomes. Clean code helps ensure that bugs can be understood and addressed in a reasonable amount of time.
Novel or Deprecated Logic
What is demanded and required of a code base is dynamic. Perhaps a new use case appears that requires new logic, maybe the user population itself has shifted and what satisfied the previous users is no longer enough. Whatever the code base, the passage of time will usually lead to the want for new code, and the disuse of other portions of code.
So what would clean code be in this context? We earlier defined clean code as being the minimum required complexity to achieve some goal. This is directly applicable to the context of adding novel logic or deprecating unwanted code.
We haven't yet discussed abstractions, structure of programs and their relation to clean code, so what this looks like may not be clear quite yet. In just a few articles things well become clearer.
Broken Window Theory
Writing Effectively
What is professional writing? It's not conveying your ideas to your readers. It's changing their ideas. Nobody cares what ideas you have.
The Craft of Writing Effectively
When writing spoken language, good authors consider context, who the reader likely is, and what they want. Just like any product, writing must deliver enough value to the reader to justify the valuable time and energy spent reading! Readers don't care what thoughts we have, no matter how novel, but rather how these thoughts may change their own.
The Craft of Writing Effectively
I highly recommend this lecture by Larry McEnerney. His discussion on the shortcomings of taught writing and their implications to technical writing are highly insightful and relevant to programmers.