PLANETS Framework for producing and judging good code.
How do you establish a standard for the quality of writing code for yourself and others? Do you have a framework for that? Set of rules or guidelines maybe?
Let me tell you about an absolutely basic trinity for judging code and coders:
- Flow Control refers to using programming statements such as:
continue
,break
,return
,goto
,exit
,halt
, etc. depending on a programming language - Conditional Statements refer to statements such as
if then else
,switch
,case
, etc. - Extraction & Specialisation refers to extracting a gut of a function or procedure out and packaging it under a dedicated and specialised function. We can also apply this in a context of objective programming and object methods.
The exact syntax may differ from one programming language to another. But the lexems mentioned above are pretty common. And besides, all the 3 aspects of a programming mentioned above are generic and universal across the board for both structural and objective programming languages.
That’s why I call them a “basic trinity” and it’s also why regardless of the label I put on it, these 3 aspects are an absolutely fundamental instrument in my evaluation of quality of programming. Not an ultimate, sole, or exclusive, but a fundamental one.
PLANETS FRAMEWORK FOR A GOOD SOURCE CODE
I have formulated a framework around the basic trinity of good code. I will lay it out for you to just grab and use. Afterwards I will follow up with a dedicated section, with code samples, and a commentary.
Without further ado, the actual framework is rather short. It consists of seven rules. And so I named it PLANETS, after planētēs (wonderers) which gave us 7 days of the week (also it’s an acronym):
Monday (Moon):
Part with conditional statements whenever possibleTuesday (Mars):
Leverage control flow in reducing and simplifying conditional statementsWednesday (Mercury):
Always exit from a condition with a sole reachable codeThursday (Jupiter):
Never hang a program on a conditional statementFriday (Venus):
Eradicate malpractice of nesting conditional statementsSaturday (Saturn):
Treat functions of boolean result type as your flow controlSunday (Sun):
Specialize functions in order to simplify conditional statements, remove redundancy, and increase readability.
This framework enforces couple of good aspects of good source code:
- Solid grasp of boolean logic. If you are a programmer then it’s a matter of your professional responsibility to have rock solid foundation in boolean logic.
- Flow control is a legacy of low level programming. Moving all the way back to the Assembly language, its
goto
statement and various jump instructions. Essentially that is how programming works at the machine level, and all high level programming languages just build up atop of this. So master the flow control and take full advantage of it. - Extraction & Specialisation. Promotes readability and conciseness. Readability is the programmer’s acknowledgement of, and regards for others. Conciseness is programmer’s professional obligation to identifying and eradicating redundancy. Specialisation is a reflection of architecture and design mastery at its most basic level.
- All Combined — those aspects are some of the most fundamental ones in the craft of programming. It’s at that fundamental and basic level where a distinction happens between an amateur, and a craftsman or an artisan. Make your code beautiful and sane by addressing these aspects.
Although I formally publish this as a named framework in the year 2022 after having been a programmer and a software development leader for over a decade, the ideas behind it date back to my college years at University of Warmia and Mazury in my home town Olsztyn. Inspired by one person from my past — Mariusz Abramczuk — who’s lectures on structural and objective programming I was lucky to have taken back around 2004. Recollection of his views on good programming and good code have been shaping my coding craft throughout my life and career.
CODE EXAMPLES
Just briefly, I’ll illustrate each rule of the PLANETS Framework with a simple code snippet. In the next chapter we will take some real world code for practice, which I think is a bigger value.
DISCLAIMER: I wanted to use gcc compiler for the snippets. But C++ does not support try-finally syntax, like Object Pascal, C#, Delphi, Java, JS do. It supports RAII, but that doesn’t work for me. While it provides the functionality, it doesn’t help with readability, like try-finally syntax would. So I just pretended that C++ does in fact support finally section and went with it. Or you can pretend, when you see it, that you’re looking at C# code.
MONDAY RULE:
TUESDAY RULE:
WEDNESDAY RULE:
THURSDAY RULE:
FRIDAY RULE:
You may also refer to the example for Sunday rule for using specialized helper functions to address this rule. And here is a different kind of example for Friday rule:
SATURDAY RULE:
In addition to the snippet below, the example used for the Sunday rule is also relevant.
Here I’m actually adding lines of code for achieving a better structure, and readability. I am using handler functions:
A concept of a handler function is that it returns true or false not based on whether or not a subprogram executed properly, but solely based on whether or not it detected the entrusted scenario, as to prevent other handlers from examining the case. Even if errors were detected or exceptions have been reached during the handling.
As you can see, in this case the number of lines of code increased, but the readability, quality and structure of code all improved.
SUNDAY RULE:
FRAMEWORK IN ACTION
I could incorporate code samples in about any programming language. I could try to search the Internet for language popularity ranking. But I am lazy, and I know that this stuff is so basic that you will understand it regardless of which language I choose. This framework is just universally relatable across languages.
And so I choose my most beloved Object Pascal, which is probably the most underestimated and overlooked modern programming languages for cross-platform, cross-architecture, and cross-purpose native (and scripting) programming. For back-end, front-end, desktop, mobile, and what-not. It’s perhaps also the most human-readable programming language among those with a practical use in applied programming.
For the workshop I chose a snippet from an official implementation of the FCL (Free Component Library), which you get shipped with the Free Pascal Compiler. In a Process.pp unit of the FCL, which implements TProcess class there is a helper function called RunCommand. It executes a command in a current directory by invoking a system process and controls its input and output pipes.
The function allows you to pass the name of the process to run, with an array of command line parameters (which can be empty), and a reference to a string variable for capturing the output of the process. It returns a boolean result indicating if the process was successfully executed and if it didn’t return any error code:
I will want to work with an older version of the FCL later on, but while we’re here, let’s briefly attend to to the current implementation.
I will work on my local copy, that I will verify at each modification stage whether or not it still compiles. Like others, I too have my personal preference for code formatting and styling. So here is the exact same code restyled/reformatted to my liking:
Let’s begin with the Monday rule: Part with conditional statements whenever possible. And so the lines 44–45 and 56–57 become:
The Result
in Object Pascal is to a function a bit like what this
is to an object in Java or JavaScript. It refers to the return value of the function, and within function’s scope it always corresponds to a correct type as per function header definition. Here, the result is of boolean type, and it’s NOT the best choice to set it using if then
conditional statement. The programmer is furthermore inconsistent because in the line #51 the more appropriate way is used.
In the line #44 I skipped the conditional statement entirely. The language syntax doesn’t require special handling for the condition that was being tested: whether or not an array being subtracted from another is empty. The language and the compiler handle both edge cases just fine. An entirely unnecessary use of a conditional statement.
And now, I will move over to an older version 3.0.2 of FCL and its implementation of RunCommand
function in Process.pp unit. The body of the function itself is very small, and we will attend to its sub-function for redesigning the code:
Now is the time for some real work. Let’s redesign the InternalRunCommand
which is a private function sitting at lines 473–570 of the process.pp unit. Let’s start from the end and just peek at the before and after.
THE BEFORE:
Actually, when dealing with a convoluted nesting of conditional statements like this, I always start with changing the text layout: where the lines break and indentation. To make it visually easier for me to navigate a complex, nested structure.
So the code above is my local copy formatted to my preferred layout. If you care to see the original layout, check here.
THE AFTER:
We extracted some code into separate helper functions. You will find a complete code at the end. Now let’s break the changes down to smaller steps for demonstrating the framework at play.
THE BREAKDOWN
First off, the original implementation is insane. It’s painful to read, mainly because the unpalatable nesting of code blocks. Code chunks are hanging under a hideous perplexity of multi-level conditional statements.
IF, THEN, ELSE, IF THEN… wait, was this an
ELSE
corresponding to thisIF
or to thatELSE-IF
?For F#@#’s sake, what is going on here ?! Was this written for a human person to read?
OK, you’ve seen the after-version, so let’s retrace the steps that led to it.
Following the framework, we attack the most offending bit first with extraction & specialization. You see, the redundancy in this code is severe and lay. Time for the Sunday rule: Specialize functions in order to simplify conditional statements, remove redundancy, and increase readability.
Let’s just see how much cleaner the code becomes:
We just took the major redundant code, which was 4 times almost-copy-paste block of code, and we extracted it into a helper function called ReadFromPipe
with arguments allowing for reusing it against both the stdout and the stderr pipes of a process: p.Output
and p.Stderr
respectively.
We roughly halved the size of the RunCommand
function body. We removed major redundancy and increased readability.
The ReadFromPipe
subroutine now contains the extracted code:
There’s more to do with this helper function. First, the utterly unnecessary conditional statement at line #51 above. It basically hangs the actual task of this helper function under a nonsensical conditional statement. This violates few rules of the framework, but most prominently it violates Wednesday rule: Always exit from a condition with a sole reachable code.
The condition here has a sole reachable code, meaning, there is no code to be reached but for a single case. And beyond that sole case, there is nothing else left to handle.
Moreover, let’s acknowledge that the code above the conditional statement is merely a simple initial sanity check. The actual work to be done by the function is behind the condition. So we are also violating the Thursday rule: Never hang a program on a conditional statement.
So let’s be professional and let’s rid of this amateur nonsense:
Look at the selected line #51, it uses flow control statement exit
to immediately return from the helper function with a FALSE result value, if the initial condition don’t qualify for the sub-routine to start.
And if we qualify, then the actual sub-routine will run in the main code block, and not as a right side to a conditional expression grammar.
We could leave this at that, and it would be okay. But we can also do an additional extraction:
While you might wonder if this is or isn’t an overkill, it’s the good practice. The extracted new helper function can potentially be reused also beyond the current scope.
But there is more benefit. Once we use the framework we end up with a much cleaner and more readable code. It then becomes easier to apply further optimisations and further patching onto the code. For example:
Look at the selected line #101. It was completely missing from the original implementation.
The code was reading data from two different pipes into two different string buffers. But only did a final trim on one of them, and not the other.
If that was by negligence of the original programmer, then probably it was contributed to by a poor readability of the code.
If it was by design, then it was a poor design. Yet the fact that it was poor got obfuscated by a poor readability of the code.
We also removed the bloat from the except
section (which is equivalent tocatch
section in other languages). Because the code inside does not really depend on exception belonging to any specific class. So evaluating the exception class was serving no purpose at all.
The code became more concise and clean, so we noticed a missing part of what was going on in a helper function. Regardless if it was something that got neglected or intended, we made the code easier for maintenance and review. And ultimately we took initiative to improve on the existing design.
Good, clean code is easier to spot and to fix design flaws. So I improved the design of the original RunCommand
function’s interface. It seems that it internally uses information that could be useful to its caller. Yet the information gets ignored and wasted. I am talking about the StdErr
output and the ExitCode
value. So in the final code I have two overloaded versions of the RunCommand2
function: one compatible with the original RunCommand
implementation, and another with the access to the two extra bits of data.
I also rid of few variables which now became internal guts of a helper function. We added some initial sanity cleaning, etc. All those count as additional optimisations beyond the scope of the original framework. But they became easier to apply in a cleaner code and a better code structure.
In the end I also moved the trimming of the buffer to the helper function, and I parametrized it:
You can see the two selected lines have a boolean argument which when set to true
will truncate the buffer to the size currently used by the data inside. The default value is set to true, so we only explicitly ask not to trim the buffer inside of the while
loop, and in the lines #95–96 we skip it, so that the buffers will get trimmed by default.
Also, we rid of all the integer variables, because these had to do with something that now only a helper function cares for.
THE GRAND FINALE
And as promised, here is the complete code with all the helper functions:
As a bonus, any of the resulting functions, including all the helper functions, are sized in a way that any of them can fit on a screen without need for scrolling. While this is not part of my framework, it is a rule of thumb for me. I always follow it by default, unless I find a good reason not to.