DEV Community

Cover image for Extreme Makeover: Code Edition
Ali Spittel
Ali Spittel

Posted on • Updated on • Originally published at zen-of-programming.com

Extreme Makeover: Code Edition

I am a little bit obsessed with writing clean code. Code should be written for future developers (including yourself) and extendability -- it should be relatively easy to add features to your application and to maintain your code. If we were writing code for computers, we would just write binary! Here are some of my tips for writing cleaner code:

1. Use clear variable and function names

Code becomes much easier to read if you write out full, descriptive variable and function names. The below code isn't very clear:

function avg (a) {
  let s = a.reduce((x, y) => x + y)
  return s / a.length
}
Enter fullscreen mode Exit fullscreen mode

It becomes a lot more readable if we write out full variable names!

function averageArray (array) {
  let sum = array.reduce((number, currentSum) => number + currentSum)
  return sum / array.length
}
Enter fullscreen mode Exit fullscreen mode

Don't minify your own code; use full variable names that the next developer can understand.

2. Write short functions that only do one thing

Functions are more understandable, readable, and maintainable if they do one thing only. If we have a bug when we write short functions, it is usually easier to find the source of that bug. Also, our code will be more reusable. For example, our above function could be renamed "sumAndAverageArray" because we are calculating the sum using reduce and then calculating the average of that sum.

function sumAndAverageArray(array) {
  let sum = array.reduce((number, currentSum) => number + currentSum)
  return sum / array.length
}
Enter fullscreen mode Exit fullscreen mode

We can break this into two functions, and it becomes more clear what each part of the code is doing. Also, if we are creating a large program, having the sumArray function could come in handy!

function sumArray(array) {
  return array.reduce((number, currentSum) => number + currentSum)
}

function averageArray(array) {
  return sumArray(array) / array.length
}
Enter fullscreen mode Exit fullscreen mode

If you are writing a function that could be named with an "and" in it -- it really should be two functions.

To summarize my tips for functions...

an infographic of the function rules

3. Documentation

Write good documentation for your code so that future developers understand what your code is doing and why.

The following code has "magic numbers" in it that aren't documented.

function areaOfCircle (radius) {
  return 3.14 * radius ** 2
}
Enter fullscreen mode Exit fullscreen mode

We could add comments to the above code to make it more understandable for someone who doesn't know the mathematical equation for finding the area of a circle.

const PI = 3.14 // PI rounded to two decimal places

function areaOfCircle (radius) {
  // Implements the mathematical equation for the area of a circle:
  // Pi times the radius of the circle squared.
  return PI * radius ** 2
}
Enter fullscreen mode Exit fullscreen mode

Note: the above code is just an example! You would probably want to use Math.PI instead of creating your own PI estimation.

Your comments should describe the "why" of your code.

Bonus: using a documentation style for your code. For Python, I love Google Style Docstrings, and JSDoc is great for JavaScript.

4. Sandi Metz's Rules

Sandi Metz -- an awesome Ruby developer, speaker, and author -- has four rules for writing clean code in object oriented languages.

  1. Classes can be no longer than 100 lines of code
  2. Methods and functions can be no longer than 5 lines of code
  3. Pass no more than 4 parameters into a method
  4. Controllers can instantiate only one object

I highly recommend watching her full talk on these rules!

I've been following these consistently for the past two years or so, and they become so ingrained that I barely think about them! I really like them, and I think they make code more maintainable.

These rules are only guidelines, but they can significantly clean up your code.

To Summarize the Sandi Metz rules...

an infographic of the Sandi Metz rules

5. Be Consistent

When writing code, consistency is key. People shouldn't be able to look at a code base and tell exactly who wrote each line of code without a git blame! If you are using semicolons in JavaScript, use them at the end of each statement. Use " vs ' consistently as well!

I would recommend using a style guide and a linter to enforce these standards -- for example, I love Standard JS for JavaScript and PEP8 for Python! I even have my text editor setup to enforce these standards whenever I save!

Find a code style and stick to it.

6. Keep your code DRY

One of the first things taught to new programmers is "don't repeat yourself". If you notice patterns in your code, use code to reduce those duplications. I often encourage my students to play the game SET to work on their pattern recognition skills.

That being said, if you DRY up your code too much or choose the wrong patterns to abstract, your code can be close to unreadable and you may need to duplicate your code more down the road. Sandi Metz has a great article on how "duplication is far cheaper than the wrong abstraction."

Don't repeat yourself, but also don't abstract your code to an extent that it is not understandable.

7. Encapsulation + Modularization

Group like variables and functions in order to make your code more reusable and understandable.

let name = 'Ali'
let age = 24
let job = 'Software Engineer'

let getBio = (name, age, job) => `${name} is a ${age} year-old ${job}`   
Enter fullscreen mode Exit fullscreen mode

If you have multiple people in your program, something like the following is more clear:

class Person {
  constructor (name, age, job) {
    this.name = name
    this.age = age
    this.job = job
  }

  getBio () {
    return `${this.name} is a ${this.age} year-old ${this.job}` 
  }
}
Enter fullscreen mode Exit fullscreen mode

or if you have only one person in your script:

const ali = {
  name: 'Ali',
  age: 24,
  job: 'Software Engineer',
  getBio: function () {
    return `${this.name} is a ${this.age} year-old ${this.job}` 
  }
}
Enter fullscreen mode Exit fullscreen mode

In a similar vein, break long programs into different files so that your code is more modular and digestible. Long files are often hard to sift through, and you may want to use small chunks of code from project to project.

Group like items in your code so that it is more reusable.

In short...

These are some good guidelines for cleaning up your code, but they aren't written in stone! I personally don't use all of these all the time (see my personal projects!), and no code is perfect. These are just some tips for writing code that can be more easily reused, read by other developers, and extended.

If you liked this article, keep in touch! I send out a newsletter every Friday with my favorite articles and what I've written that week. Also, Tweet me your favorite tips for writing clean code!

Discussion (40)

Collapse
kip13 profile image
kip • Edited

Useful and good tips !

I'm agree with all except with comments, if you follow the rule of Use clear variable and function names isn't necessary put comments, is redundant.

From Clean Code - Robert C. Martin:

  • Always try to explain yourself in code.
  • Don't be redundant.

Is just my opinion, always is better be more expressive in our code.

Collapse
aspittel profile image
Ali Spittel Author

I like this article about comments.

Only at the point where the code cannot be made easier to understand should you begin to add comments.

Collapse
kip13 profile image
kip

If the code is abstract to the main idea, of course !

Sometimes you need comments but most of the time you need to think twice if is necessary.

From your shared post

Sometimes when I’m writing code, I think “I should comment this”

Right. The first and proper response to that feeling is to roll up your sleeves and refactor the code. After you’ve refactored, if you still feel a comment is necessary, then by all means add one. Never comment before you’ve attempted to refactor away the comment first.

Collapse
ben profile image
Ben Halpern
Thread Thread
kip13 profile image
kip • Edited

Good post, thanks for sharing Ben.

Maybe this can be an simple example:

(function() {
    // Is necessary apply the `bind` method
    // to set `this` in the right context
    const $ = document.querySelector.bind(document);

    // more code...
})();

This saves the time of going to the documentation or being confused, but it is not redundant, is the main idea that I'm trying to share, IMHO the comments are useful always in the correct way.

Thread Thread
ben profile image
Ben Halpern

I love React's dangerouslySetInnerHTML which replaces a lot of documentation and comments and forces the user to understand what's going on. If you don't know why it says "dangerous", you can look it up.

Much better than

//This is dangerous, read this post about XSS:
setInnerHTML

Because the comment would never get ported across all the use cases (obviously, given it's a library).

But most code doesn't live up to the scrutiny of library code and comments, used properly, are a perfectly reasonable part of the toolbelt.

Thread Thread
kip13 profile image
kip

Yes, yes and yes !

I have never used React but dangerouslySetInnerHTML is so clear !

Thread Thread
nickytonline profile image
Nick Taylor (he/him)

Yeah, I've only ever used it once and it was to inject release notes into a component from an external HTML file. yolo.

Collapse
sudiukil profile image
Quentin Sonrel

Awesome posts, these rules are simple and yet they can make a huge difference in the code quality!

As you said, these are not written in stone but still, there is one rule that bothers me a lot:

Methods and functions can be no longer than 5 lines of code

I like the idea of reducing the size of classes, functions, methods, etc... but 5 lines (10 even) is just impossible most of the time and in my opinion over reducing functions/methods sizes makes the code harder to read in most cases. Splitting 15 lines over 3 functions makes it hard to keep up for someone unfamiliar with the code. Overusing syntaxic shortcuts is no good either if clarity is your main goal.

Collapse
aspittel profile image
Ali Spittel Author

Ah interesting, I personally am very much in the 5 lines or less camp -- I think it is rare that your function is doing one thing only if it is longer than that! I rarely even write functions that are a full five lines of code to be honest!

Collapse
kayis profile image
K

I followed this practice for a few years.

Now I'm more into big functions when there is no/not much duplication involved.

I use blocks to encapsulate parts of the bigger functions and early returns, so you often don't need to scroll down the whole function.

Jumping around a file or multiple files to find 10 functions that are only used once or twice when they could have been a few extra lines in the main function doesn't cut it for me anymore.

Collapse
jdsteinhauser profile image
Jason Steinhauser

While I generally hold as close to this rule as I can, there are times where I've had to break the rule to get the job done. Those have mostly been in financial applications, and numerical methods (ordinary differential equation solvers, spherical harmonics, etc.). Basic CRUD apps, I've hardly ever written any function that requires more than 5 lines.

Collapse
alchermd profile image
John Alcher

Yeahh, 5 lines is too short IMO. Especially for the more verbose languages. To be fair, a function can be reasonably long and still do only one thing.

Collapse
dance2die profile image
Sung M. Kim

These tips are the gists of many of clean code practices 👍.

If you (as in developers like you and me) want to go deeper, you can read Uncle Bob's clean code book (as mentioned in other comments), or listen to this extensive podcast eps by Coding Blocks guys (epsidoes 47~55) where CB guys explain each coding practice mentioned in Clean Code book.

You can probably listen to these during your commute 🚂 to work 👷‍♀.

They might be 2 years old but these principles are evergreen skills that would last throughout your careers.

  1. 47. Clean Code – Writing Meaningful Names
  2. 48. Clean Code – How to Write Amazing Functions
  3. 49. Clean Code – Comments Are Lies
  4. 50. Clean Code – Formatting Matters
  5. 51. Clean Code – Objects vs Data Structures
  6. 52. Clean Code – Error Handling
  7. 53. Clean Code – Integrating with Third Party Libraries the Right Way
  8. 54. Clean Code – How to Write Amazing Unit Tests
  9. 55. Clean Code – How to Write Classes the Right Way
Collapse
codemouse92 profile image
Jason C. McDonald

In regards to comments, check out the Commenting Showing Intent standard I helped author. I've been using it in production for a few years, with an excellent return on investment.

For anyone who says "clean code shouldn't have comments", please consider -- the cleanest, most obvious code in the world still fails to explain "why". The intent in your code is only obvious to you because you wrote it; to believe anyone else can or should figure it out is expecting nothing short of mind-reading. MANY problems in modern coding can be attributed to this mindset.

Collapse
alchermd profile image
John Alcher

I'm personally in the "Clean comments shouldn't have code" camp.

Collapse
kip13 profile image
kip • Edited

Is not about to avoid the comments(this are useful of course), is about avoid the redundancy and obvious noise.

Collapse
codemouse92 profile image
Jason C. McDonald • Edited

Sure, but that's the difference between "what" (redundant noise) and "why" (never ever redundant to anyone unfamiliar with the code).

Granted, it can be a fine line, but I've found it's better to air on the side of too many comments, leave them for a few weeks, and then come back later and pare back the unhelpful ones that have proven redundant (therefore showing them to be "what" comments).

Collapse
thecodetrane profile image
Michael Cain

All good stuff. Perhaps I’m spoiled in Ruby on Rails, but I feel like if your methods if you do 1, 2 and 4-7 well, you shouldn’t need comments. Comments, IMHO, are a last resort to explain something that is, by necessity, opaque.

Collapse
ben profile image
Ben Halpern

This is an all-time great post

Collapse
aspittel profile image
Ali Spittel Author

Thank you!!!

Collapse
buinauskas profile image
Evaldas

Third one! Put PI to function call, you'll have a pure function then. :)

Collapse
aspittel profile image
Ali Spittel Author

Ahh interesting! I normally make my constants global, but could see that being good too!

Collapse
buinauskas profile image
Evaldas • Edited

Both approaches are good. Putting const in function would align with functional programming approach. Making function not dependant on anything that's outside of it (being pure) 😊

Collapse
unicorndough profile image
Kyara.

Always love to read your articles on here! You're an inspiration for me. 😄

Collapse
aspittel profile image
Ali Spittel Author

Thank you so much!!!

Collapse
ben profile image
Ben Halpern

Collapse
itsjzt profile image
Saurabh Sharma

Find a code style and stick to it

Prettier does it really nicely

Collapse
aspittel profile image
Ali Spittel Author
Collapse
nickytonline profile image
Nick Taylor (he/him)

Yeah prettier, aside from choosing a code style also helps eliminate a lot of noise in PRs so you can focus on the real problem the PR is trying to solve instead of "can you format this line like this? I find it reads better". It is opinionated, but I just love how it takes that off the table.

We use it at work and even dev.to uses it now.

Collapse
dimist profile image
mistriotis

+1 for the Sandi Metz talk :)

Collapse
foxdonut00 profile image
Fox Donut • Edited

Great article @Ali !

Just one correction, regarding meaningful names, this is actually misleading:

function averageArray (array) {
  let sum = array.reduce((number, currentSum) => number + currentSum)
  return sum / array.length
}

In a reduce, the first parameter is the accumulated value, and the second is the next item from the array. So it should be:

function averageArray (array) {
  let sum = array.reduce((currentSum, number) => currentSum + number)
  return sum / array.length
}

In your example, by chance it didn't hurt because A) both the array and the accumulated value are numbers, and B) addition is commutative, but if your operation was not, or if your accumulated value was of a different type, this would become more important.

I hope that is helpful! Thank you for writing interesting and useful articles!

Collapse
jassehomar profile image
Omar Jasseh

Marvelous, and thanks so much Ali :) ... I will definitely sign up for your newsletters

Collapse
bc7 profile image
Brian

Great Tips! I'm a teaching assistant at a coding bootcamp an we actually mention a lot of these to help teach best practices.

Collapse
dulemart profile image
Dule Martins

Am grateful!

Collapse
jmscavaleiro profile image
jmscavaleiro

Thanks! Very useful article!

Collapse
whoisryosuke profile image
Ryosuke

All great tips. Keep it up! 🙌

Collapse
nightsquid7 profile image
Nightsquid7

These tips are great!

Collapse
drewknab profile image
Drew Knab

Great list of tips. In addition to these, I'm a really big fan of early returns and limiting else statements whenever possible.

Collapse
johand profile image
Johan

Awesome 👏