Tips to write good code

That you can start applying today

Focus on content

  • Code like you're writing a story for someone else that doesn't know what you're doing
  • Avoid generic variables (a, b, c), unless the content is obvious
  • Avoid step1, step2, ... function names. Make explicit what you're doing
  • A developer should know what your functions do just reading the signature

Focus on content

This is ๐Ÿ’ฉ

              
                // I know it's the first step, and I know it's first result!
                // I don't understand what first step is!
                const result1 = doStep1();
                // ๐Ÿ’ฉ same as before...
                const result2 = doStep2(result1);
                // Come on... Get value of what? ...a? ๐Ÿ˜ญ
                // I have to read the function bodies to understand...
                // Time wasted! ๐Ÿคฌ
                const a = getValue(result2);
              
          

Focus on content

This is ๐Ÿคฉ

              
                // Ok, now I know that you're extracting data from a UserForm
                const formData = getUserFormData();
                // And you're validating it
                const validationErrors = validate(formData);
                // And checking if we can recover errors
                // without user intervention.
                // I don't have to read anything else! ๐Ÿพ
                const shouldAskToUser = shouldPromptToUser(validationErrors);
              
          

Use guards clauses

  • They improve code readability
  • Assert input as soon as possible, fail fast, then you can focus only on business logic
  • They avoid unnecessary code branching

Use guards clauses

Don't do this

              
                function saveCar(Car car, Price price, InvoiceRepo invoiceRepo): Invoice {
                  if (car.isValid) {
                    if (price.isBelowThreshold) {
                      const invoice = prepareInvoice(car, price);
                      if (invoiceRepo.isAvailable) {
                        return invoiceRepo.save(invoice);
                      }
                      // Else everywhere!!!
                      else {
                        throw new InvoiceUnavailableError();
                      }
                      // Nesting everywhere ๐Ÿคฏ
                    }
                    else {
                      throw new PriceIsTooLowError(price);
                    }
                    // My eyes are burning! ๐Ÿ˜ญ
                  } else {
                    throw new InvalidCarError(car);
                  }
                }
              
          

Use guards clauses

Do this

              
                function saveCar(Car car, Price price, InvoiceRepo invoiceRepo): Invoice {
                  // Guard clauses in action!
                  if (!car.isValid) {
                    throw new InvalidCarError(car);
                  }
                  if (!price.isBelowThreshold) {
                    throw new PriceIsTooLowError(price);
                  }
                  if (!invoiceRepo.isAvailable) {
                    throw new InvoiceUnavailableError();
                  }

                  // And then focus on business logic.
                  // No nesting, no else, no messy things! ๐Ÿพ
                  return invoiceRepo.save(prepareInvoice(car, price));
                }
              
          

Exploit language features

  • Every language has unique features to improve code quality. Spend time to learn them.
  • You'll write less code => less bugs, less code to maintain
  • I'll show you some examples from different languages

Exploit language features

Scala: this is ๐Ÿคจ...

              
                val carWithGasolineNames = users.flatMap { user => 
                  user.cars.withFilter(car => car.hasGasoline)
                    .map(car => car.name)
                }
              
          

Exploit language features

Scala: this is ๐Ÿคฉ

              
                // Same as before, but using for comprehension!
                val carWithGasolineNames = {
                  user <- users
                  car <- user.cars if car.hasGasoline
                } yield car.name
              
          

Exploit language features

TypeScript / JS: this is ๐Ÿคจ...

              
                const newBook = Object.assign({}, defaultBook);
                newBook.title = patchBook.title;
                newBook.description = patchBook.description;
                newBook.author = patchBook.author;
                newBook.releasedAt = patchBook.releasedAt;
                return newBook;
              
          

Exploit language features

TypeScript / JS: this is ๐Ÿคฉ

              
                // Same as before, but using destructuring...
                const { title, description, author, releasedAt } = patchBook;
                // and spread operator!
                return {
                  ...defaultBook,
                  title,
                  description,
                  author,
                  releasedAt,
                };
              
          

Exploit language features

Dart: this is ๐Ÿคจ...

              
                List additionalCars = myFriendCars;
                if (myFriendCars == null) {
                  additionalCars = otherFriendCars;
                }
                List myFavoriteCars = [];
                myFavoriteCars.add(ferrari488);
                myFavoriteCars.add(lamborhiniHuracan);
                myFavoriteCars.add(lanciaDeltaHFIntegrale);
                myFavoriteCars.addAll(additionalCars);
                return myFavoriteCars;
              
          

Exploit language features

Dart: this is ๐Ÿคฉ

              
                // Same as before, but using cascading operator (..)
                return []
                  ..add(ferrari488)
                  ..add(lamborhiniHuracan)
                  ..add(lanciaDeltaHFIntegrale)
                  // and if null operator!
                  ..addAll(myFriendCars ?? otherFriendCars)
              
          

Ask dependencies from constructor

  • You make explicit class dependencies from beginning
  • You can mock dependencies very easily in unit tests!

Ask dependencies from constructor

This is ๐Ÿ’ฉ...
              
                assert('BookService should save book', () => {
                  const book = new Book("id", "title", "description");
                  new BookService().save(book);
                  // I can't mock BookRepo dependency...
                  // It's created inside BookService!
                  // I have to use real BookRepo and the real DB ๐Ÿ˜ญ
                  expect(new BookRepo().get(book.id)).toEqual(book);
                });
              
          

Ask dependencies from constructor

This is ๐Ÿคฉ
              
                  assert('BookService should save book', () => {
                    const book = new Book("id", "title", "description");
                    const mockedBookRepo = mock(BookRepo);
                    // I can pass a mocked BookRepo, test are easier now!
                    new BookService(mockedBookRepo).save(book);
                    expect(mockedBookRepo.save).calledOnceWith(book);
                    // No DB involved, all is mocked ๐Ÿพ
                  });
              
          

Avoid negative variables names

  • Do not call variables like notFinished, notAllowed or even worse notInvalid
  • Use this: draft, forbidden, valid
  • Negative variable names create unnecessary confusion, especially when we negate them in code.

Avoid passing too many parameters

  • Use data classes instead
  • Data classes are easier to pass to another function
  • If parameters are strictly correlated, it's better to group them inside class

Avoid passing too many parameters

This is ๐Ÿ’ฉ...
              
                // To much parameters!
                function filterBooks(name, description, createFrom,
                                    createTo, authorEmail) {
                  // I have to pass to another function
                  // I have to rewrite them all! ๐Ÿ˜ญ
                  return bookRepo.filter(
                    name, description, createFrom,
                    createTo, authorEmail
                  );
                }
              
          

Avoid passing too many parameters

This is ๐Ÿคฉ
              
                  // All parameters are inside bookFilters data class!
                  function filterBooks(bookFilters) {
                    // Passing them is much easier now ๐Ÿพ
                    return bookRepo.filter(bookFilters);
                  }
              
          

Keep in touch