Hey guys, my name is Vinicius and I started working as a dev in 2018.
This is my first post ever, I don't really like to expose myself, I'm trying to change that over time, but as it's a really nice environment here, I decided to bring some good practices that I use in my day to day.
1 - Function with three or more parameters, I prefer passing an object.
When I have to develop a function with three or more parameters, it is a bit difficult to pass the parameters to the function and when it is not a function that I developed, but I need to use it, I need to go back to the function declaration to know what is necessary to pass in each field .
Example of a function with more than three parameters:
const createInvoice = (
name: string,
lastName: string,
totalValue: number,
address: string
) => {
console.log(name, lastName, totalValue, address);
};
createInvoice(`Vinicius`, `Lopes`, 1500, `Pennsylvania Avenue NW`);
The moment we call the function, if we are the creator of the function, it is even easier to remember, but the values are loose and it is necessary to go straight to the function declaration to know what is necessary to pass in each field.
As this function exceeds the three parameters we can refactor using an object.
Example of how to call the function with the parameters being in an object:
type CreateInvoiceParams = {
name: string;
lastName: string;
totalValue: number;
address: string;
};
const createInvoice = ({
name,
lastName,
totalValue,
address,
}: CreateInvoiceParams) => {
console.log(name, lastName, totalValue, address);
};
createInvoice({
name: `Vinicius`,
lastName: `Lopes`,
totalValue: 1500,
address: `Pennsylvania Avenue NW`,
});
Yes! We seem to write more! But it does have some advantages…
It seems that the code is more verbose but we have some advantages that I will list.
- First, we have an encapsulated type of our method that we can reuse in a few other places.
- The order of the values now does not matter in the invocation and it is much easier to know which parameters are needed without having to see the function declaration, reducing the cognitive effort in future maintenance.
- Readability, when one looks at the function invocation one can immediately identify the parameters used.
2 - Functional programming > imperative programming 😲
I'm kidding, actually it would be to use the functional artifices of the language when possible.
Javascript is a language that is not 100% functional, but we can use some tricks that help with readability and in our daily lives.
I'll show you an example of a sum of values done in an imperative manner.
const orders = [
{
name: `Vinicius`,
value: 50,
},
{
name: `Ailton`,
value: 80,
},
{
name: `Lucas`,
value: 100,
},
{
name: `Felipe`,
value: 50,
},
];
let total = 0;
for (let i = 0; i < orders.lenght; i++) {
total += orders[i].value;
}
This face of for
is already well known because we learned it in college, but we can do something simpler and that will probably improve our performance and knowledge of how we can work with arrays in JS.
const orders = [
{
name: `Vinicius`,
value: 50,
},
{
name: `Ailton`,
value: 80,
},
{
name: `Lucas`,
value: 100,
},
{
name: `Felipe`,
value: 50,
},
];
const total = orders.reduce((acc, order) => acc + order.value, 0);
the one above is reduce it is a method for arrays in JS, at first it seems a little complicated but with a brief explanation to understand, let's explain.
const total = orders.reduce((acc, order) => acc + order.value, 0);
As it is a functional method, first it receives a function that in javascript we usually call a callback function, this function receives an accumulator (acc
) and a command field, after this function it receives the initial value of the accumulator, which is 0
.
Soon after the callback function is run receiving field by field of the array changing the accumulator by the return of the reduce function which would be acc + order.value
and after going through all the fields of the array executing the function and saving the accumulator it returns what was accumulated for the total
variable
In addition to reduce
, we have map
, flatMap
, filter
, find
, sort
, all of which help a lot in everyday life and improve readability and reduce cognitive effort in the code.
3 - Do not use ELSE!!
Okay this can be a little tricky at first and will need some practice, don't use else
is a rule of object calisthenics and I think it's one of the coolest among the 9 rules described.
Without the else
you can reduce the flow of conditions in your code and thus reduce the complexity, the biggest problem with the else
that I see is that it is EVERYTHING that is not if
, on certain occasions it makes total sense to have an else
, depending on the rule in business you might need an else
but most of the time we wouldn't need it.
A cool technique is to use the early return, it consists of always returning as quickly as possible from a method, always exit quickly from within a method.
One idea I use is to check the possible conditions as a business rule and errors at the beginning and the “happy path” is at the end.
Okay, I've explained enough, let's see a code to make it clearer.
const checkDeliveryPrice = (value: number) => {
if (value > 90) {
notChargeDelivery();
} else {
chargeDelivery();
}
};
This code above we check if the value is above 90 we do not charge shipping and if it is not greater than 90 we will charge shipping, but we can improve it.
const checkDeliveryPrice = (value: number) => {
if (value > 90) {
notChargeDelivery();
return;
}
chargeDelivery();
};
OK, it was a little cleaner without the else
and we managed to reduce the cognitive effort of understanding this method, we also reduced the flows that the application can follow.
3.1 Early return are the friends we make along the way.
The early return consists of failing quickly, ( what does that mean ? ) validate the possible problems before the “happy way” we go straight to the code.
const checkDeliveryPrice = (value: number, franchised: boolean) => {
if (value > 90) {
if (franchised) {
notChargeDelivery();
}
if (!franchised) {
alert(`You are not a franchisee, you will be charged shipping`);
chargeDelivery();
}
} else {
if (value !== undefined) {
alert(`Invalid value`);
return;
}
}
chargeDelivery();
};
Do you realize that it's even ugly to look at a code like that?
In this code we check if the guy spent more than 90
and if he is a franchisee he does not pay shipping, but if he is a franchisee he pays the freight, and in the else
we check if the value is invalid and we warn the user of the invalid value, if it is valid he only charges the shipping normally.
Imagine for our brain that we use it all day, the cognitive effort we use to understand.
We can refactor this code thinking about an early return rule, fail quickly, example:
const checkDeliveryPrice = (value: number, franchised: boolean) => {
if (valor !== undefined) {
alert(`Invalid value`);
return;
}
if (value > 90) {
if (franchised) {
notChargeDelivery();
}
if (!franchised) {
alert(`You are not a franchisee, you will be charged shipping`);
chargeDelivery();
}
} else {
chargeDelivery();
}
};
We take the invalid field validation to the beginning of the method and return the error to the user as soon as possible and we don't need to enter the whole code, but we can improve the validations even more.
const checkDeliveryPrice = (value: number, franchised: boolean) => {
if (valor !== undefined) {
alert(`Invalid value`);
return;
}
if (!franchised) {
alert(`You are not a franchisee, you will be charged shipping`);
chargeDelivery();
return;
}
if (value > 90) {
notChargeDelivery();
return;
}
chargeDelivery();
};
Here we do the validations first and leave the “happy path” for last, we check if the user is a franchisee, if it is, skip to the other if
and check if the value is above 90
and does not charge shipping and if it is not above 90
, it charges shipping normally.
We don't need to nest any if
and we can better manipulate the conditions and our effort to understand is much less.
4 - Replacing the switches in our code
In Javascript, sometimes we get lost in making a switch/case
or even several if
that work to check a constant value and return something, in these cases we can use the object literal, not to be confused with a template literal.
Object literal is nothing more than a javascript object, but as it has the object's key and value, we can create some “checks” that make the code much easier and cleaner.
Example with if
const verifyUser = (userType: "admin" | "user" | "guest") => {
if (userType === "admin") return "Administrator";
if (userType === "user") return "User";
if (userType === "guest") return "Anonymous";
};
Example with switch/case
const verifyUser = (userType: "admin" | "user" | "guest") => {
switch (userType) {
case "admin":
return "Administrator";
case "user":
return "User";
case "guest":
return "Anonymous";
default:
return "Anonymous";
}
};
Just an example, however, you can see that they are static values, without validations and we are parsing the values for other information.
Even so, it was a strange and complicated code for the little thing it does, apart from code repetition and, in the case of switch/case
, the error of forgetting a break;
It is huge.
We can bring the object literals to help us with this and improve the readability of the code, example:
const userTypes = {
admin: "Administrator",
user: "User",
guest: "Anonymous",
};
const verifyUser = (type: "admin" | "user" | "guest") => {
return userTypes[type] || "Anonymous";
};
Wow, it looks like magic, right, but what we do is pass the type to the object and by the field key it returns the value and the default value will be Anonymous if there is no return.
We can see that it has improved the readability and also the maintenance of this code, it is a code that can scale quickly just by including new values in the variable userTypes
and we don't need to repeat code.
It ends here 🤙
I hope I have added some value to our community, I wrote the first things that came into my head and made some examples to illustrate.
If you have good practices that you always remember to use in everyday life, comment that I also want to know.
Thanks for reading. 🤓
Top comments (0)