Skip to content

ioet/clean-code-challenge

Repository files navigation

CLEAN CODE

Naming 📓

Naming things (= variables, properties, functions, methods, classes) correctly and in an understandable way if an extremely important part of writing clean code.

Be descriptive, names have one simple purpose: They should describe what's stored in a variable or property or what a function or method does. Or what kind of object will be created when instantiating a class.

Some ground rules 👊!

Captura de Pantalla 2022-03-04 a la(s) 12 44 26

Exception: Boolean variables, contants, functions and methods are better named as a Yes/No question

  • isUserValid = True
  • bool inputIsValid() {}

In most situations, you should avoid generic names like handle(), process(), data, item etc. It doesn't matter if you prefer fetch...(), get...(), retrieve...() or any other term but you should be consistent!

Examples:

Comments & Formatting 💣

You could think that comments help with code readability. In reality, the opposite is often the case though.

Proper code formatting (i.e. keeping lines short, adding blank lines etc.) on the other hand helps a lot with reading and understanding code.

Bad comments Good comments
Dividers & Markers Legal Information
Redundant Information "Required" Explanations
Commented Out Code Warnings
Misleading Comments To do Notes

Vertical and Horizontal formating

Captura de Pantalla 2022-03-04 a la(s) 10 13 08

Vertical Formating example

Functions (& Methods)

Functions and methods (I don't differentiate here) are the meat of any code we write. All our code is part of some function or method after all. And we use functions to call other functions, build re-usable functionalities and more.

Functions are made up of three main parts:

  • Their name
  • Their parameters (if any)
  • Their body

How Many Parameters Are Okay:question:

Captura de Pantalla 2022-03-04 a la(s) 10 45 28

Functions example

Keep Functions Small

Besides the number of paramters, the function body should also be kept small. In order to be small, functions should just do one thing. Exactly one thing.

Example (large login function, no abtraction):

function login(email, password) {
  if (!email.includes('@') || password.length < 7) {
    throw new Error('Invalid input!');
  }
  const existingUser = database.find('users', 'email', '==', email);
  if (!existingUser) {
    throw new Error('Could not find a user for the provided email.');
  }
  if (existingUser.password === password) {
    // create a session
  } else {
    throw new Error('Invalid credentials!');
} }

Example (small function, but is doing one thing?):

function login(email, password) {
  validateUserInput(email, password);
  verifyCredentials(email, password);
  createSession();
}

A function is considered to do just one thing if all operations in the function body are on the same level of abstraction and one level below the function name.

Control Structures

No matter which kind of application you're building - you will most likely also use control structures in your code: if statements, for loops, maybe also while loops or switch-case statements.

But control structures can also lead to bad or suboptimal code and hence play an important role when it comes to writing clean code.

Prefer positive checks

This is a simple one. It can make sense to use positive wording in your if checks instead of negative wording.

if isEmpty(blogContent):
  // throw error

if not hasContent(blogContent):
  // throw error

let avoid negative checks

if isNotEmpty(blogContent):
  // continue
}
if not isNotEmpty(blogContent) :
  // throw error (?)
}

Avoid Deep Nesting

This is very important! You should absolutely avoid deeply nested control structures since such code is highly unreadable, hard to maintain and also often error-prone. dirty code

function messageUser(user, message) {
  if (user) {
    if (message) {
      if (user.acceptsMessages) {
        const success = user.sendMessage(message);
        if (success) {
          console.log('Message sent!');
        }
   } }
 } }

Use Guards & Fail Fast

Guards are a great concept! Often, you can extract a nested if check and move it right to the start of a function to fail fast if some condition is (not) met and only continue with the rest of the code otherwise.

function messageUser(user, message) {
  if (!user || !message || !user.acceptsMessages) {
      return;
  }
  user.sendMessage(message);
  if (success) {
    console.log('Message sent!');
  }
}

Guards in action

Extract Control Structures & Logic Into New Functions

We already learned that splitting functions and keeping functions small is important. Applying this knowledge is always great, it also helps with removing deeply nested control structures.

Extracting functions

Improving logic?

Embrace Errors

Errors are another nice way of getting rid of redundant if checks. They allow us to utilize mechanisms built into the programming language to handle problems in the place where they should be handled (and cause them in the place where they should be caused...).

Using error

Polymorphism & Factory Functions

Sometimes, you end up with duplicated if statements and duplicated checks just because the code inside of these statements differs slightly.

Factory function

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •