Merge pull request #16 from inato/best-practices-guilde

 Add best practices to readme
This commit is contained in:
Scott Picquerey 2022-04-06 15:57:59 +02:00 committed by GitHub
commit a3298a6bba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 103 additions and 2 deletions

View file

@ -1,4 +1,5 @@
{ {
"singleQuote": true, "singleQuote": true,
"trailingComma": "all" "trailingComma": "all",
"arrowParens": "avoid"
} }

102
README.md
View file

@ -7,20 +7,120 @@ The exercices consist of unimplemented functions and their associated failing
tests. tests.
To run the tests, simply run To run the tests, simply run
```sh ```sh
$ yarn test $ yarn test
``` ```
You can also run them in watch mode: You can also run them in watch mode:
```sh ```sh
$ yarn test:watch $ yarn test:watch
``` ```
Finally, if you wish to only run the tests for a given exercice `exoN`, you can Finally, if you wish to only run the tests for a given exercice `exoN`, you can
run the following: run the following:
```sh ```sh
$ yarn test[:watch] exoN $ yarn test[:watch] exoN
``` ```
The exercices are organized into `exoN` folders and most of what is required to The exercices are organized into `exoN` folders and most of what is required to
complete each is detailed in the comments. complete each is detailed in the comments.
## code style guide
For readability purpose, we replace `ReaderTaskEither` by `rte`
- Use `flow` instead of `pipe` when possible
> Why? Using flow reduces the amount of variables to declare in a method, hence the visibility and readability of the code
```typescript
// Bad
const formatUserPhoneNumber = (user: User) =>
pipe(user, User.phoneNumber, User.formatPhoneNumber);
// Good
const formatUserPhoneNumber = flow(User.phoneNumber, User.formatPhoneNumber);
```
- Avoid using `boolean` method `match` when unecessary
> Why? boolean.match can lower the global understanding of a method and enforce nested pipes. Using classic `if/else` is often the best option
```typescript
// Bad
const triggerEmailCampaign = ({
user,
...emailSettings
}: {
user: User} & EmailSettings) =>
pipe(
user.nationality === 'FR',
boolean.match(
() => triggerGlobalEmailCampaign({ to: user.email, emailSettings }),
() => triggerFrenchEmailCampaign({ to: user.email, emailSettings }),
),
);
// Good
const triggerEmailCampaign = ({
user,
...emailSettings
}: { user: User } & EmailSettings) => {
if (user.nationality === 'FR') {
return triggerFrenchEmailCampaign({ to: user.email, emailSettings });
}
return triggerGlobalEmailCampaign({ to: user.email, emailSettings });
```
- Avoid nested pipes
> Why? They lower global understanding of the code. We allow ourselves 2 levels of piping maximum per function and tend to do atomic functions instead
```typescript
// Bad
export const convertDollarAmountInCountryCurrency = ({
countryName,
amountInDollar,
}: {
countryName: CountryName;
amountInDollar: number;
}) =>
pipe(
getCountryCode(countryName),
either.map(
countryCode =>
pipe(
getCountryCurrency(countryCode),
option.map(
flow(
convertFromDollarAmount(amountInDollar),
convertedAmount =>
console.log(
`converted amount for country ${countryCode} is ${convertedAmount}`,
),
),
),
),
),
),
);
// Good
export const convertDollarAmountInCountryCurrency = (amountInDollar: number) =>
flow(
getCountryCode,
either.map(convertDollarAmountToCountryCodeCurrency(amountInDollar)),
);
const convertDollarAmountInCountryCodeCurrency =
(amountInDollar: number) => (countryCode: CountryCode) =>
pipe(
getCurrencyFromCountryCode(countryCode),
option.map(convertFromDollarAmount(amountInDollar)),
option.map(convertedAmount =>
console.log(
`converted amount for country ${countryCode} is ${convertedAmount}`,
),
),
);
```