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 ๐ฉ
const result1 = doStep1();
const result2 = doStep2(result1);
const a = getValue(result2);
Focus on content
This is ๐คฉ
const formData = getUserFormData();
const validationErrors = validate(formData);
const shouldAskToUser = shouldPromptToUser(validationErrors);
- 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
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 {
throw new InvoiceUnavailableError();
}
}
else {
throw new PriceIsTooLowError(price);
}
} else {
throw new InvalidCarError(car);
}
}
Do this
function saveCar(Car car, Price price, InvoiceRepo invoiceRepo): Invoice {
if (!car.isValid) {
throw new InvalidCarError(car);
}
if (!price.isBelowThreshold) {
throw new PriceIsTooLowError(price);
}
if (!invoiceRepo.isAvailable) {
throw new InvoiceUnavailableError();
}
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 ๐คฉ
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 ๐คฉ
const { title, description, author, releasedAt } = patchBook;
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 ๐คฉ
return []
..add(ferrari488)
..add(lamborhiniHuracan)
..add(lanciaDeltaHFIntegrale)
..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);
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);
new BookService(mockedBookRepo).save(book);
expect(mockedBookRepo.save).calledOnceWith(book);
});
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 ๐ฉ...
function filterBooks(name, description, createFrom,
createTo, authorEmail) {
return bookRepo.filter(
name, description, createFrom,
createTo, authorEmail
);
}
Avoid passing too many parameters
This is ๐คฉ
function filterBooks(bookFilters) {
return bookRepo.filter(bookFilters);
}