Lancer un projet pilote dans un délai limité peut inspirer un maximum de scrappiness. C’est ainsi que nous avons appris quelques erreurs de code courantes
Récemment, une de mes équipes a eu une nouvelle opportunité passionnante de travailler sur un nouveau projet. Nous avons convenu de passer un mois à faire fonctionner quelque chose qui pourrait prouver que la théorie du produit était une bonne idée. Nous avons choisi le maximum de scrappiness.
Bien que certaines choses que je mentionnerai ne se limitent pas aux technologies elles-mêmes, je divulguerai que nous avons utilisé TypeScript, React et Material UI pour ce projet.
Lorsque vous avez une bonne idée de produit, il est préférable de sortir rapidement quelque chose dans un état technique approximatif.
« Mais tout devra être refait plus tard », sont les pensées intérieures de quelqu’un qui lit. Mais comme je le dis à mes enfants lorsque leur tour Kapla est tombée, vous vous dépasserez la prochaine fois. Autrement dit, lorsque vous faites quelque chose qui ressemble à une refonte, vous le construisez plus rapidement, ce sera plus pertinent et la qualité sera meilleure.
Après avoir réalisé « hé, ce produit pourrait fonctionner », nous avons commencé à passer en revue certaines de nos erreurs les plus courantes lors du codage et nous avons partagé ces idées entre nous.
Incorrect
Voici un exemple de classe pour typer un objet :
export class Animal {
animalName?: string;
type?: "horse" | "dog";
}const main = () => {
const animal: Animal = {
animalName: "bingo",
type: "horse",
};
return animal;
};
export default main;
Corriger
Si vous devez taper un objet, utilisez type
ou alors interface
au lieu. TypeScript exclura vos déclarations de type de la sortie finale, mais les classes resteront et encombreront inutilement votre sortie. Voici à quoi cela ressemble :
export type Animal = {
animalName?: string;
type?: "horse" | "dog";
}const main = () => {
const animal: Animal = {
animalName: "bingo",
type: "horse",
};
return animal;
};
export default main;
Incorrect
Je veux préparer rapidement du code pour utiliser une nouvelle intégration. J’ai obtenu mon jeton et quelques paramètres. Mettons simplement les éléments dans le même fichier pour gagner du temps. Voici le code pour le faire :
import { AwesomeApiClient } from "./AwesomeApiClient";const main = () => {
new AwesomeApiClient("my-token", { godMode: true });
};
export default main;
Corriger
Le temps que vous économisez en ne créant pas de fichier de configuration pour des choses comme celle-ci, ou un .env
fichier, dans certains cas, est négligeable.
Créez simplement ce fichier immédiatement à la place et évitez les tracas.
// awesomeApi.config.ts
import { Token } from "./AwesomeApiClient";
import { Settings } from "./AwesomeApiClient";export const token: Token = "my-token";
export const settings: Settings = { godMode: true };
// correct.ts
import { token, settings } from "./awesomeApi.config";
import { AwesomeApiClient } from "./AwesomeApiClient";
const main = () => {
new AwesomeApiClient(token, settings);
};
export default main;
Lorsque async
/ await
est venu, beaucoup de gens se sont trompés, y compris moi. Alors que certains peuvent être longs « sur ça », il vaut la peine de répéter un malentendu de base.
Incorrect
Je déclare une fonction asynchrone et je me dis : « hé, je dois probablement attendre la réponse dans le code de cette fonction. Sinon, je ne rendrai pas ce qui est attendu, n’est-ce pas ?
Consommant fetchData()
Je le fais await fetchData()
.
import { fetch } from "./util";export const fetchData = async () => {
return await fetch("www.google.com");
};
Corriger
Les async
mot clé fait que la fonction renvoie une promesse quoi qu’il arrive. C’est pour await
fonctionner correctement (tout est promis sous le capot).
Dans ce cas, fetchData
contient un fetch
appel qui renvoie une promesse. Alors, pourquoi ne pas retourner cette promesse ?
import { fetch } from "./util";export const fetchData = () => fetch("www.google.com");
Tu peux encore faire await fetchData()
dans votre code de la consommation car tout ça await
est d’attendre qu’une promesse soit résolue (ou de la rejeter et de générer une erreur).
Cela ne nécessite aucun exemple de code. Les gens sont extrêmement habitués à la convention selon laquelle un fichier doit avoir une classe. Dans la plupart des cas, il vaut mieux s’en tenir à cette règle plutôt que d’avoir le réveil brutal de se rendre compte, « oh wow, il y a une autre classe ici que je n’ai pas vue » après avoir essayé de comprendre une base de code.
Lorsque vous créez des fonctions, vous voulez qu’elles soient faciles à lire. L’une des méthodes pour y parvenir est de s’assurer qu’il a le bon niveau d’abstraction.
Par exemple, si vous avez une fonction qui génère des personnes au hasard, votre code initial pourrait ressembler à ceci
Incorrect
Si nous regardons à l’intérieur du generatePeople
fonction, il y a une charge cognitive immédiate pour comprendre ce qu’elle fait réellement. Nous pourrions peut-être contourner cela avec des commentaires, décrivant ce que fait chaque bloc de code, mais il existe un autre moyen.
export type GeneratePeopleFunction = (count: number) => People;export type Person = {
age: number;
name: string;
};
export type People = Person[];
export const generatePeople: GeneratePeopleFunction = (count: number) => {
const names = ["Adam", "Bertil", "Cesar", "David"];
const result: {
age: number;
name: string;
}[] = [];
for (let i = 0; i < count; i++) {
result.push({
age: Math.floor(Math.random() * 100) + 1,
name: names[Math.floor(Math.random() * names.length)],
});
}
result.sort((a, b) => (a.name > b.name ? 1 : -1));
return result;
};
Corriger
Ici, j’ai décomposé les différentes fonctionnalités et les ai placées dans des fonctions utilitaires distinctes à partir d’un fichier que j’ai nommé util.ts
.
Maintenant, vous pouvez saisir immédiatement ce que generatePeople
fait : il génère des personnes en fonction d’une liste de noms et les trie par nom. La charge cognitive est considérablement réduite.
import { names } from "./data";
import { GeneratePeopleFunction, People } from "./types";
import { generatePerson, sortPeopleByName } from "./util";export const generatePeople: GeneratePeopleFunction = (count: number) => {
const result: People = [];
for (let i = 0; i < count; i++) {
result.push(generatePerson(names));
}
return sortPeopleByName(result);
};
Notez que ce n’est peut-être pas ainsi que j’écris immédiatement mon code, mais très probablement, comment je refactoriserais mon code initial pour mieux paraître dans le résultat final. Bien que vous puissiez suivre certaines directives principales sur la façon de répartir les choses dans des endroits séparés, cela vient également de l’expérience et d’un instinct développé au fil du temps. Ne vous culpabilisez pas si votre séparation n’est pas la meilleure ; c’est pourquoi vous avez des développeurs seniors qui vous aident avec la programmation en binôme et en foule (et parfois les révisions de code de demande d’extraction).