Règles de code
Conventions réellement appliquées et pratiquées dans ce codebase. La philosophie (Clean Code, Object Calisthenics, patterns) vit dans
CLAUDE.md; cette page est le « comment on écrit concrètement le code Feelbat ».
Règles d'or
- Utiliser les tokens de couleur du thème — ne jamais coder une couleur en dur. (
AGENTS.md) - Utiliser le hook
useTranslationpour l'i18n — ne pas importertdirectement depuisi18next. - Préférer les alias de modules (
components/…,store/…) aux chemins relatifs profonds. importplutôt querequire.- Toute nouvelle fonctionnalité non triviale → un module de feature (
src/features/), pas un nouveau sous-arbre descreens/.
Fichiers & nommage
| Élément | Convention | Exemple |
|---|---|---|
| Vues React | .tsx |
UnitSelector.tsx |
| Utilitaires purs / domaine | .ts |
validators.ts |
| Composants | PascalCase | ChangeMode, CategoryItem |
| Valeurs / hooks / fonctions | camelCase | updatePreference, useChangeModeForm |
| Constantes d'action Redux | SCREAM_CASE | ADD_MEASURE, SET_LANGUAGE |
| Constantes d'opération GraphQL | SCREAM_CASE | CHANGE_DELTA_V_MODE, GET_UNIT_PREFERENCE |
Gardez un nom cohérent partout — si c'est sensorId à un endroit, c'est sensorId partout.
Structure d'un fichier de composant
Un composant par fichier, un StyleSheet par fichier (en bas). Aucune logique dans le JSX — extrayez-la en variables/fonctions au-dessus du return. Aucune fonction anonyme dans le rendu — utilisez useCallback. Enveloppez les lignes de liste dans React.memo.
// 1. imports : externes → internes aliasés → relatifs
import { useMemo } from 'react';
import { StyleSheet, View } from 'react-native';
import { useAppTheme, AppTheme } from 'components/Navigation/types/theme';
// 2. type des props
interface UnitSelectorProps {
units: Unit[];
value: UnitId;
onChange: (id: UnitId) => void;
}
// 3. composant : hooks d'abord, early returns, puis JSX avec attributs testID
export const UnitSelector: React.FC<UnitSelectorProps> = ({ units, value, onChange }) => {
const theme = useAppTheme();
const styles = useMemo(() => unitSelectorStyles(theme), [theme]);
// …
};
// 4. factory de StyleSheet en bas
const unitSelectorStyles = (theme: AppTheme) =>
StyleSheet.create({
btnActive: {
backgroundColor: theme.colors.activeColor,
borderRadius: theme.roundness * 2, // les rayons sont toujours roundness * n, jamais des nombres bruts
},
});
Thème
Les tokens vivent dans src/theme/lightColors.ts et darkColors.ts ; on y accède via useAppTheme() (depuis components/Navigation/types/theme), qui expose colors et roundness. Les factories de styles prennent theme et sont mémoïsées (useMemo(() => styles(theme), [theme])).
✅ backgroundColor: theme.colors.activeColor
❌ backgroundColor: '#2468C1'
✅ borderRadius: theme.roundness * 2
❌ borderRadius: 8
Les tokens sémantiques incluent activeColor (bleu primaire), black, orange, green, grayLight, white, plus les groupés thresholds.*, acceleration{X,Y,Z}.
i18n
✅ const { t } = useTranslation('changeMode'); // dans un composant
✅ getWhatChangeItems(t) // les helpers prennent t en paramètre
❌ import { t } from 'i18next';
6 langues (en, fr, de, it, pt, es), fallbackLng: 'en', config dans src/i18n/i18n.ts. Les clés sont regroupées en namespaces par domaine (fichiers JSON) sous src/i18n/<lang>/ — passez le namespace à useTranslation('<ns>'). Les namespaces de feature vivent sous src/i18n/<lang>/features/<feature>.json.
État
- Global / persisté → duck Redux (
src/store/ducks/) : constante d'action + action creator + reducerswitch+ selectors dans un seul fichier. PascreateSlice. - Local à une feature → store zustand dans la feature (
hooks/*.store.ts) ou React Context. - N'ajoutez pas de duck Redux pour un état qui appartient à une seule feature.
Voir architecture.md.
GraphQL
- Les opérations sont co-localisées avec leur consommateur :
*.mutation.ts,*.fragment.ts, ouapi/queries/*dans une feature. - Les noms d'opérations doivent être globalement uniques — le preset
clientde codegen échoue si deux documents partagent un nom (ex.query getSensordéfini deux fois). Gardez une seule définition canonique par opération ; ne copiez pas une opération dans un second fichier. - Seuls les
.tsimbriqués sont scannés (documents: "src/**/*/*.ts"). Une opération dans un.tsxou un fichier de premier niveau ne sera pas générée. - Les types proviennent du dossier généré
src/gql/— ne jamais éditer ce dossier ; lancezyarn generate-interfaces:<env>après avoir modifié des opérations, et rafraîchissezgraphql.schema.json.
Principes de conception (issus de CLAUDE.md — appliqués, pas aspirationnels)
- Les fonctions font une seule chose ; des verbes pour les noms ; ≤ 3 arguments (sinon un objet d'options).
- Early returns plutôt que conditions imbriquées ; pas de
else; un niveau d'indentation par fonction. - Pas de nombres magiques — des constantes nommées.
- Pas de paramètres booléens qui masquent l'intention — préférez deux fonctions.
- Les fonctions longues (> 20 lignes) sont un avertissement, > 40 un problème.
- Appliquez les patterns GoF seulement s'ils simplifient : Strategy pour les switches par type (voir
useChangeModeColorsindexé sur le mode), Observer/Context pour la communication inter-composants, Factory pour la création complexe.
Application
Prettier + ESLint via lint-staged au commit ; Conventional Commits via commitlint ; architecture via dependency-cruiser ; contrôles de PR via DangerJS. Détails dans ci-cd.md.
Fichiers clés
CLAUDE.md,AGENTS.md— les règles sources.eslintrc,.prettierrc.jssrc/theme/,src/i18n/i18n.ts- feature de référence :
src/features/unitPreference/