A Simple Rule for Clean(er) Code
I was tempted by the glittering allure of a clickbait title - "SENIOR CODING IN ONE STEP" or something to that effect. This time, the forces of darkness didn't win, and I opted for something honest and direct.
I've been writing code for a long time, almost 16 years, and I've heard every rule of thumb, heuristic, trick, essential skill and nonsense process there is. Only one of them has really stuck with me.
Code Like a Contract
See, no weird build up. I'm just going to tell you what it is. Shocking in this day and age, I know. If you really want to have your mind blown, I didn't use Chat-GPT to write this either. Someone get me ink and a quill, I'm f***ing Shakespeare right now.
Let's start with some code. This code isn't bad by any stretch, but it helps to illustrate the point.
bannedIps = [] // Imagine some IPs here!
function isSafeRequest(httpRequest) {
if (bannedIps.includes(httpRequest.ip) {
return false;
}
if(/bot|spider|crawl/i.test(httpRequest.headers["User-Agent"])) {
return false;
}
if(/select|update|delete|where/i.test(httpRequest.body)) {
return false;
}
return true;
}
It's relatively clear what's going on here. We're testing if the incoming HTTP request is "safe" and measuring it.
So what's the problem?
I think the banned IP check is clear. The bot user agent clause is a little less clear, and the SQL syntax clause is not at all clear. Worse, these are extremely simplified examples. This code will only become more convoluted. So what is the alternative? Code a contract.
Contracts are convoluted and confusing, just like your code
Contracts are FULL of jargon, difficult language and lengthy definitions. They are written this way because precision and exact language is vital. Kind of like writing code, so how do they solve it?
Contracts typically move their definitions to either the start, or the end. Something like:
领英推荐
The Buyer is defined as the individual buying the house on the 26/05/2024
You'll see a dozen or so of these definitions in your employment contract, the deeds to your home and more. Now, the power of separating these definitions is that when it comes to making the crucial statements of the contract, the heavy lifting has been done.
The Buyer will buy The Property from The Seller On 26/05/2024 for the Agreed Sum Provided All Conditions (see Appendix A) have been met.
The power of either front loading, or pushing back definitions, is the ability to create simple statements that abstract the complexity. Now, we can apply this logic to our code.
We have to create definitions, in this case functions, that clearly describe the clauses. Then, our parent function will become our contractual statement.
bannedIps = [] // Imagine some IPs here!
function isBlockedIp(ipAddress) {
return bannedIps.includes(ipAddress);
}
function isBotUserAgent(userAgent) {
return /bot|spider|crawl/i.test(userAgent);
}
function hasSqlChars(data) {
return /select|update|delete|where/i.test(data);
}
function isSafeRequest(httpRequest) {
return !isBlockedIp(httpRequest.ip)
&& !isBotUserAgent(httpRequest.headers["User-Agent"])
&& !hasSqlChars(httpRequest.body);
}
Now, no deciphering is necessary to determine the conditions for a safe request. We can even push this logic further if we want:
bannedIps = [] // Imagine some IPs here!
function isBlockedIp(request) {
return bannedIps.includes(request.ip);
}
function isBotUserAgent(request) {
return /bot|spider|crawl/i.test(request.headers["User-Agent"]);
}
function hasSqlChars(request) {
return /select|update|delete|where/i.test(request.body);
}
checksForSafeRequest = [isBlockedIp, isBotUserAgent, hasSqlChars]
function isSafeRequest(httpRequest) {
return checksForSafeRequest.all(check => !check(httpRequest))
}
We're entering the realm of stylistic choice, but I really like this latest iteration. What are the checks that I will perform on my request? There's a list of checks called checksForSafeRequest. The beauty of this last example is that it will scale much more cleanly. There won't be one if statement with a million conditions. There will be a list detailing exactly what will be checked.
Well, that's that. If you liked reading this, give me a follow. If you didn't, then leave a comment so I can ignore it.
Wait one important thing. I am writing this as me, Chris Cooney, software engineer and binge drinker. I am not writing this on behalf of Coralogix, so if you're going to sue anyone for the contents of this article (bad code isn't a crime, otherwise I'd be away for life), sue me, and good luck - I haven't got any money.