My App

Naming

kebab-case partout pour les fichiers et routes, exceptions documentées (composants React, tables DB, variables TS)

Règle générale : kebab-case pour tout ce qui est ressource (fichier, dossier, route URL). Les exceptions sont les identifiants de langage qui suivent leurs propres conventions (composants React en PascalCase, tables Postgres en snake_case, etc.).

La règle en une ligne par type

TypeConventionExemple
Fichier TS / TSXkebab-case.tscardex-service.ts, user-card.tsx
Dossierkebab-case/packages/api/src/modules/cardex/
Route URL (Elysia + Next)/kebab-case/cardex/confirm-arrival, /auth/check-in
Nom de skill (Claude)kebab-casebell-feature-module, bell-pms-adapter
Variable TypeScriptcamelCaseguestId, checkInStatus
FonctioncamelCaseconfirmGuestArrival(), linkGuestToUser()
Type / InterfacePascalCaseNormalizedGuest, IntegrationAdapter
ClassePascalCaseMewsAdapter, IntegrationSyncService
Composant React (export)PascalCaseexport function UserCard() dans user-card.tsx
Constante moduleSCREAMING_SNAKE_CASEMAX_CONCURRENT_JOBS, DEFAULT_LOCALE
Env varSCREAMING_SNAKE_CASEDATABASE_URL, STRIPE_SECRET_KEY
Table DBsnake_caseguest_conversation, room_service_order
Colonne DBsnake_casecheck_in_status, external_property_id
pgEnum valuessnake_casepending, checked_in, in_progress
Index DBsnake_case avec suffixeguest_email_idx, room_org_number_uq
Branche gitkebab-case avec préfixefeat/cardex-vip-toggle, fix/check-in-race

Les 3 règles pour les fichiers

1. kebab-case par défaut

packages/api/src/modules/cardex/
├── cardex.routes.ts              ✅
├── cardex.service.ts             ✅
├── cardex-state-machine.ts       ✅
├── CardexService.ts              ❌ (PascalCase)
├── cardexService.ts              ❌ (camelCase)
└── cardex_service.ts             ❌ (snake_case)

2. Composants React : fichier kebab-case, export PascalCase

packages/ui/src/components/user-card.tsx
// ✅ fichier kebab-case, export PascalCase
export function UserCard({ name }: { name: string }) {
  return <div>{name}</div>;
}

Ne jamais exporter default sur un composant (sauf pages Next.js où c'est la convention du framework).

3. Suffixes de rôle, pas de préfixes

# Bon : le rôle est le SUFFIXE, le module est le PRÉFIXE
cardex-service.ts
cardex-routes.ts
cardex-repository.ts
cardex-schemas.ts
check-in-state-machine.ts

# Mauvais
service-cardex.ts             ❌
use-cardex.ts                 ⚠️  OK pour hooks React, sinon non

Exception hooks React : use- prefix standard de la communauté React.

// apps/pwa/src/features/check-in/use-check-in-status.ts
export function useCheckInStatus() { /* ... */ }

Routes Elysia et Next.js

Elysia

Tous les segments URL en kebab-case, pluralisation pour les collections :

// ✅
.get("/guests", ...)
.post("/guests/:id/confirm-arrival", ...)
.get("/check-in/stream", ...)           // SSE endpoint
.post("/integrations/configure", ...)

// ❌
.post("/confirmArrival", ...)           // camelCase dans URL
.post("/confirm_arrival", ...)          // snake_case
.post("/ConfirmArrival", ...)           // PascalCase

Next.js App Router

Même chose. Les route groups entre parenthèses sont aussi en kebab-case :

apps/pwa/src/app/
├── (guest)/                       ✅
│   ├── layout.tsx
│   └── room-service/page.tsx      ✅
└── auth/
    ├── fast-check-in/page.tsx     ✅
    └── check-in-upsells/page.tsx  ✅

Dynamic segments : [id], [...slug] — standard Next.js, non négociable.

Les exceptions au kebab-case

1. Convention du langage ou framework

  • Composants React exportés en PascalCase (convention React)
  • Hooks React : useXxx camelCase (convention React)
  • Variables, fonctions TS en camelCase (convention TS/JS)
  • Tables Postgres en snake_case (convention SQL)
  • Pages Next.js : page.tsx, layout.tsx, loading.tsx (convention framework)

2. Syntaxe imposée par Elysia

Elysia autorise les segments dynamiques et wildcards avec : et *. Ça reste du kebab-case autour :

.get("/guests/:guest-id", ...)          // ❌ le segment dynamique est camelCase en fait
.get("/guests/:guestId", ...)           // ✅ convention Elysia
.get("/files/*", ...)                   // ✅ wildcard standard

Les params dans les handlers sont en camelCase (convention TS) :

.get("/guests/:guestId", ({ params: { guestId } }) => /* ... */)

3. Fichiers de config imposés

Noms imposés par les outils, on ne touche pas :

next.config.ts
tsconfig.json
biome.json
drizzle.config.ts
package.json
docker-compose.yml

4. SKILL.md

Claude attend exactement SKILL.md (majuscules strictes). C'est la seule exception aux règles de casing dans le projet. Voir apps/fumadocs/content/docs/skills/ pour la conception des skills Bell.

Tables et colonnes DB

packages/db/src/schema/guests.ts
export const guest = pgTable("guest", {
  id: uuid("id").defaultRandom().primaryKey(),
  organizationId: uuid("organization_id").references(() => organization.id),
  firstName: text("first_name").notNull(),
  lastName: text("last_name").notNull(),
  email: text("email").notNull(),
  checkInStatus: checkInStatusEnum("check_in_status").default("pending"),
  externalGuestId: text("external_guest_id"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().$onUpdate(() => new Date()).notNull(),
});

Côté TypeScript : camelCase (guest.checkInStatus). Drizzle fait la conversion automatiquement via le second argument "check_in_status".

Branches git

Format : <type>/<module>-<description-courte>.

TypeUsage
feat/Nouvelle fonctionnalité
fix/Correction de bug
refactor/Refacto sans changement de comportement
docs/Doc uniquement (fumadocs, CLAUDE.md, README)
test/Ajout / correction de tests
chore/Maintenance (deps, config, CI)

Exemples :

feat/cardex-bulk-vip
fix/check-in-race-condition
refactor/integrations-bridge-retry
docs/adr-state-machines
test/mews-adapter-contract
chore/upgrade-elysia-1.5

Commits (Conventional Commits)

Format : <type>(<scope>): <description>.

feat(cardex): add bulk VIP toggle
fix(check-in): prevent race on linkGuestToUser
docs(adr): add state machines decision
test(mews-adapter): cover contract for reservations
chore(ci): bump bun to 1.3.8

Pas de majuscule au début, pas de point final, mode impératif ("add" pas "added").

Outillage

Le respect de ces règles est partiellement automatisé :

  • Biome : formatage, imports, ordre des exports
  • TypeScript strict : détecte les any, null, undefined
  • Husky + lint-staged : pre-commit biome check --write
  • CI GitHub Actions : bloque les PR qui ne passent pas bun check et bun check-types

Ce qui n'est pas automatisable (naming des fichiers, cohérence des routes URL) est à la charge du reviewer. Si un pattern devient récurrent, on écrit une règle Biome custom.

On this page