DEV Community

Cover image for Writing Clean Code: Best Practices and Principles

Writing Clean Code: Best Practices and Principles

MFONIDO MARK on September 16, 2023

Introduction Writing clean code is a fundamental skill for every software developer. Clean code not only makes your codebase more mainta...
Collapse
 
cezarytomczyk profile image
Cezary Tomczyk
# Good comment
# Calculate the total score by incrementing x
total_score = x + 1
Enter fullscreen mode Exit fullscreen mode

I am not sure what's good in that example, as the code is self-explanatory. :-)

I could see a good example like that:

# Good comment
# Converting a string to a number because the API returns the incorrect data type of a string when a number type is expected
totalUsers = Number(users);
Enter fullscreen mode Exit fullscreen mode

The comment explains why value needs to be converted and prevents questions like "We don't need to convert value to number here".

Collapse
 
moopet profile image
Ben Sinclair

Even then I'd refactor it into its own function and call it something like fixBrokenApiReturnType.

Collapse
 
cezarytomczyk profile image
Cezary Tomczyk

The function name fixBrokenApiReturnType does not explain why it needs to be fixed. I'd eventually add a comment with the JIRA (or the like) ticket ID.

Thread Thread
 
barrymichaeldoyle profile image
Barry Michael Doyle

Don't forget to JSDoc the living **** out that function as well xD

Thread Thread
 
cezarytomczyk profile image
Cezary Tomczyk

@barrymichaeldoyle Roger that xD

Collapse
 
lionelrowe profile image
lionel-rowe

Passive-aggressive function names, love it 😆 (I'm not being sarcastic, seeing people's exasperation come through in their comments/naming always gives me a good chuckle, and I've been known to do the same myself on occasion)

Collapse
 
ronaldroe profile image
Ronald Roe

Even in your example, the comment isn't totally necessary. If that's JavaScript, it's clear what's happening. If it's another language, we could just cast to string, and that would be clear from the code. For instance:

int totalUsers = (int) users;

Collapse
 
cezarytomczyk profile image
Cezary Tomczyk

Even in your example, the comment isn't totally necessary. If that's JavaScript, it's clear what's happening.

My example code is clear, but only from the code perspective. The question is, why would you convert an incoming value to a number here? And to clarify, an additional comment would help here.

Collapse
 
camco profile image
Camco

Please give the credit to the author of this content...

As I was reading this, I knew it was directly from Clean Code: A Handbook of Agile Software Craftsmanship... So I pulled the book from my desk and confirmed it

en.m.wikipedia.org/wiki/Robert_C._...

Collapse
 
favourmark05 profile image
MFONIDO MARK • Edited

Hi @camco
I appreciate your comment. However, I would like to clarify that the content I provided is a reflection of general knowledge and widely accepted industry practices. It has not been directly copied from any specific source, including 'Clean Code: A Handbook of Agile Software Craftsmanship.'

Collapse
 
defufna profile image
Ivan Savu

Now this is a ChatGPT written comment if I ever seen one...

Thread Thread
 
wakywayne profile image
wakywayne

So true 🤣

Collapse
 
syeo66 profile image
Red Ochsenbein (he/him) • Edited

Shorter functions are easier to understand, test, and maintain.

I'd say this is not always true. Just spitting everything into smaller functions can lead to way too much fragmentation and it requires you to jump across several functions and files to grasp the full picture. The same can happen because of the DRY principle.

Sometimes it makes sense to duplicate the same code for a) readability reasons and/or b) because it serves a different purpose and just happen to look the same.

In short: those things lead to premature abstraction (YAGNI is often the better approach)

Collapse
 
crisz profile image
crisz

I conceptually divide the classes into two categories: master and utility.
The master code looks like this:

  • master:
    • utility.step1();
    • utility.step2();
    • if (condition) utility.step3();
    • utility.step4();

In this way, just reading the master code you can have a full picture of the flow and what it does.

On the other hand, often I see code like this:

  • foo:
    • bar:
      • fn1()
      • fn2:
      • fn3()
      • fn4()
      • fn5()
    • fn 6()

This is a code impossible to read and to maintain

Collapse
 
favourmark05 profile image
MFONIDO MARK

Thank you for your input. You've highlighted a valid point about the potential drawbacks of excessive code fragmentation and strict adherence to DRY. Balancing clean code with readability and avoiding premature abstraction is indeed a nuanced challenge. Context and project requirements often dictate the best approach. Your insights contribute to a valuable discussion on software development principles

Collapse
 
nigelthorne profile image
Nigel Thorne • Edited

Good artical, but your examples are off.

Good comments explain "why" not how or what. Method names explain what. Method bodies explain how. It's often not taught like this as introductory programming books often use comments to explain what some piece of code is doing. Once you can read code you should not need that help.

Changing a method to have a discount argument is not an example of refactoring as it changes behaviour. In TDD, the red, green, refactor cycle, relies on the tests written in red, that you made pass in green. If you add behaviour in refactoring, you won't have a test for it. Refactoring is defined as changing the design or structure without changing the behaviour.

Keep up the work.

Collapse
 
favourmark05 profile image
MFONIDO MARK

Thanks for the feedback, Your insights are valuable.

Collapse
 
marcobrandizi profile image
Marco Brandizi • Edited

catch ( ex ) {
console.error(...)
}

This should only be done at the top level of your application, and in most cases it is already being done, eg, by your web container or CLI framework.

If the only thing you can do with an exception is reporting it, then IGNORE it and let the top level deal with reporting. This will do it in the way that was configured for the entire application (eg, it sends it to the app logger).

If you can detect a more specific anomaly, eg, your case where you have something like "Invalid input %s for ", then RETHROW ANOTHER exception, ATTACHING the original one (the stacktrace is very valuable for debugging).

If you can recover from an exception, then do it in the catch and log the workaround as a warning (eg, "Config file not found, using default config"). If you need to interrupt the normal flow, then catch and rethrow or return, DO NOT catch and let your regular code to continue (with bad data, inconsistent/insecure state, etc).

I see wrong ways of dealing with exceptions (empty catch, console.error(), out.println () ) all the time and it's so time-wasting and frustrating.

Collapse
 
sjames1958gm profile image
Stephen James

I have been doing this for 47 years, and clean code has seldom been a fundamental skill.

Collapse
 
ronaldroe profile image
Ronald Roe

I submit that if you're properly naming your variables and other structures, that constitutes good commenting. Comments should be rare, because your code should be so readable, you don't need them.

I know you aren't invoking Uncle Bob, here, but I'm going to. He covers that concept right in preface of the book, before he ever digs into the subject.

Collapse
 
danielmabadeje profile image
Daniel Mabadeje

Thank you Mark,

Very Insightful 🔥👏👏

Collapse
 
favourmark05 profile image
MFONIDO MARK

Thank you Daniel

Collapse
 
hasanelsherbiny profile image
Hasan Elsherbiny

well explained article 👍

Collapse
 
narrinddhar_52 profile image
Narendra gorantla

Good

Collapse
 
binarybitbytes profile image
BinaryBitBytes

Very informative, thank you

Collapse
 
larry_lo_5896c556cf188250 profile image
Larry Lo

Well explained !

Collapse
 
kenekanem profile image
Kennedy Ekanem

Thank you chief 🔥

Collapse
 
chiragagg5k profile image
Chirag Aggarwal

Maturity is when you start writing clean code. I am immature 😅

Collapse
 
syeo66 profile image
Red Ochsenbein (he/him) • Edited

I'd say maturity is when you realize some clean code principles are not always rights

Collapse
 
orimdominic profile image
Orim Dominic Adah

Well explained!

Collapse
 
jcaicedo4t profile image
Jose Caicedo

Perfect

Collapse
 
utshapaul profile image
Utsha Paul

Your understanding level is so much cool. really I am enjoying this article

Collapse
 
johnscoleman profile image
John Coleman

Printing to the console when catching exceptions is not error handling, it’s a form of exception swallowing and is a common bad practice that will typically result in unpredictable behaviours.

Collapse
 
darasot profile image
darasot

Thanks for sharing.
Very Insightful.

Collapse
 
asomlev profile image
Aleks Somlev

Very Insightful
Also you could pick up other point of veiw on explaining principles here codingstories.io/space/clean-code

Collapse
 
favourmark05 profile image
MFONIDO MARK

Thanks alot @asomlev

Collapse
 
webwanderer0007 profile image
webWanderer0007

I am glad to say I do follow all these steps.

Collapse
 
pucheo_gideon profile image
Gideon Okwongudo

It's been an educative read for me. Thank you, Mark!

Collapse
 
chiemelie profile image
Chiemelie

This is great. These are simple stuff but will boost you productivity and those around you.

Collapse
 
spectateswamp profile image
Spectate

Keep every version...
Storage is dirt cheap
It's easy to use a Diff app to find those pesky changes.
I write dirty code and it surprises me even.

archive.org/details/spectate-01-se...