Que fait Conventions de Codage & Bonnes Pratiques ?
Conventions de codage de base applicables à tous les projets.
Ce skill constitue le socle commun, et non le guide détaillé d'un framework.
- Utilisez
frontend-patternspour React, la gestion d'état, les formulaires, le rendu et l'architecture UI. - Utilisez
backend-patternsouapi-designpour les couches de référentiel/service, la conception des endpoints, la validation et les préoccupations spécifiques au serveur. - Utilisez
rules/common/coding-style.mdlorsque vous avez besoin de la couche de règle réutilisable la plus courte plutôt que d'un parcours complet.
Quand l'Activer
- Démarrage d'un nouveau projet ou module
- Révision du code pour la qualité et la maintenabilité
- Refactoring du code existant pour suivre les conventions
- Application de la cohérence des noms, du formatage ou de la structure
- Mise en place de règles de linting, de formatage ou de vérification de types
- Intégration de nouveaux contributeurs aux conventions de codage
Limites du Périmètre
Activez ce skill pour :
- les noms descriptifs
- l'immutabilité par défaut
- l'application de la lisibilité, KISS, DRY et YAGNI
- les attentes en matière de gestion des erreurs et la révision des odeurs de code
N'utilisez pas ce skill comme source principale pour :
- la composition React, les hooks ou les motifs de rendu
- l'architecture backend, la conception d'API ou la stratification de base de données
- les conseils propres à un framework lorsqu'une compétence ECC plus spécifique existe déjà
Principes de Qualité du Code
1. Lisibilité avant tout
- Le code est plus lu qu'écrit
- Noms de variables et de fonctions clairs
- Code auto-documenté privilégié par rapport aux commentaires
- Formatage cohérent
2. KISS (Keep It Simple, Stupid)
- La solution la plus simple qui fonctionne
- Éviter le sur-engineering
- Pas d'optimisation prématurée
- Facile à comprendre > code astucieux
3. DRY (Don't Repeat Yourself)
- Extraire la logique commune dans des fonctions
- Créer des composants réutilisables
- Partager des utilitaires entre les modules
- Éviter la programmation copier-coller
4. YAGNI (You Aren't Gonna Need It)
- Ne construisez pas de fonctionnalités avant qu'elles ne soient nécessaires
- Évitez la généralité spéculative
- N'ajoutez de la complexité que lorsque c'est nécessaire
- Commencez simple, refactorez quand nécessaire
Standards TypeScript/JavaScript
Nommage des Variables
// PASS : BON : Noms descriptifs
const marketSearchQuery = 'election'
const isUserAuthenticated = true
const totalRevenue = 1000
// FAIL : MAUVAIS : Noms flous
const q = 'election'
const flag = true
const x = 1000
Nommage des Fonctions
// PASS : BON : Motif verbe-nom
async function fetchMarketData(marketId: string) { }
function calculateSimilarity(a: number[], b: number[]) { }
function isValidEmail(email: string): boolean { }
// FAIL : MAUVAIS : Flou ou nom seul
async function market(id: string) { }
function similarity(a, b) { }
function email(e) { }
Motif d'Immutabilité (CRITIQUE)
// PASS : TOUJOURS utiliser l'opérateur spread
const updatedUser = {
...user,
name: 'Nouveau Nom'
}
const updatedArray = [...items, newItem]
// FAIL : NE JAMAIS muter directement
user.name = 'Nouveau Nom' // MAUVAIS
items.push(newItem) // MAUVAIS
Gestion des Erreurs
// PASS : BONNE : Gestion d'erreur complète
async function fetchData(url: string) {
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
return await response.json()
} catch (error) {
console.error('Échec de récupération :', error)
throw new Error('Impossible de récupérer les données')
}
}
// FAIL : MAUVAISE : Aucune gestion d'erreur
async function fetchData(url) {
const response = await fetch(url)
return response.json()
}
Bonnes Pratiques Async/Await
// PASS : BON : Exécution parallèle quand c'est possible
const [users, markets, stats] = await Promise.all([
fetchUsers(),
fetchMarkets(),
fetchStats()
])
// FAIL : MAUVAIS : Séquentiel quand ce n'est pas nécessaire
const users = await fetchUsers()
const markets = await fetchMarkets()
const stats = await fetchStats()
Sécurité des Types
// PASS : BON : Types appropriés
interface Market {
id: string
name: string
status: 'active' | 'resolved' | 'closed'
created_at: Date
}
function getMarket(id: string): Promise<Market> {
// Implémentation
}
// FAIL : MAUVAIS : Utilisation de 'any'
function getMarket(id: any): Promise<any> {
// Implémentation
}
Bonnes Pratiques React
Structure des Composants
// PASS : BON : Composant fonctionnel avec types
interface ButtonProps {
children: React.ReactNode
onClick: () => void
disabled?: boolean
variant?: 'primary' | 'secondary'
}
export function Button({
children,
onClick,
disabled = false,
variant = 'primary'
}: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
className={`btn btn-${variant}`}
>
{children}
</button>
)
}
// FAIL : MAUVAIS : Pas de types, structure floue
export function Button(props) {
return <button onClick={props.onClick}>{props.children}</button>
}
Hooks Personnalisés
// PASS : BON : Hook personnalisé réutilisable
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => clearTimeout(handler)
}, [value, delay])
return debouncedValue
}
// Utilisation
const debouncedQuery = useDebounce(searchQuery, 500)
Gestion d'État
// PASS : BON : Mises à jour d'état correctes
const [count, setCount] = useState(0)
// Mise à jour fonctionnelle pour un état basé sur l'état précédent
setCount(prev => prev + 1)
// FAIL : MAUVAIS : Référence directe à l'état
setCount(count + 1) // Peut être obsolète dans des scénarios asynchrones
Rendu Conditionnel
// PASS : BON : Rendu conditionnel clair
{isLoading && <Spinner />}
{error && <ErrorMessage error={error} />}
{data && <DataDisplay data={data} />}
// FAIL : MAUVAIS : Enfer ternaire
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
Standards de Conception d'API
Conventions REST
GET /api/markets # Lister tous les marchés
GET /api/markets/:id # Obtenir un marché spécifique
POST /api/markets # Créer un nouveau marché
PUT /api/markets/:id # Mettre à jour un marché (complet)
PATCH /api/markets/:id # Mettre à jour un marché (partiel)
DELETE /api/markets/:id # Supprimer un marché
# Paramètres de requête pour le filtrage
GET /api/markets?status=active&limit=10&offset=0
Format de Réponse
// PASS : BON : Structure de réponse cohérente
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
meta?: {
total: number
page: number
limit: number
}
}
// Réponse de succès
return NextResponse.json({
success: true,
data: markets,
meta: { total: 100, page: 1, limit: 10 }
})
// Réponse d'erreur
return NextResponse.json({
success: false,
error: 'Requête invalide'
}, { status: 400 })
Validation des Entrées
import { z } from 'zod'
// PASS : BON : Validation par schéma
const CreateMarketSchema = z.object({
name: z.string().min(1).max(200),
description: z.string().min(1).max(2000),
endDate: z.string().datetime(),
categories: z.array(z.string()).min(1)
})
export async function POST(request: Request) {
const body = await request.json()
try {
const validated = CreateMarketSchema.parse(body)
// Continuer avec les données validées
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({
success: false,
error: 'Échec de validation',
details: error.issues
}, { status: 400 })
}
}
}
Organisation des Fichiers
Structure du Projet
src/
├── app/ # Routeur d'application Next.js
│ ├── api/ # Routes API
│ ├── markets/ # Pages des marchés
│ └── (auth)/ # Pages d'authentification (groupes de routes)
├── components/ # Composants React
│ ├── ui/ # Composants UI génériques
│ ├── forms/ # Composants de formulaires
│ └── layouts/ # Composants de mise en page
├── hooks/ # Hooks React personnalisés
├── lib/ # Utilitaires et configurations
│ ├── api/ # Clients API
│ ├── utils/ # Fonctions d'aide
│ └── constants/ # Constantes
├── types/ # Types TypeScript
└── styles/ # Styles globaux
Nommage des Fichiers
components/Button.tsx # PascalCase pour les composants
hooks/useAuth.ts # camelCase avec le préfixe 'use'
lib/formatDate.ts # camelCase pour les utilitaires
types/market.types.ts # camelCase avec le suffixe .types
Commentaires & Documentation
Quand Commenter
// PASS : BON : Expliquer POURQUOI, pas QUOI
// Utiliser un backoff exponentiel pour éviter de submerger l'API pendant les pannes
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
// Mutation délibérée ici pour des raisons de performance avec de grands tableaux
items.push(newItem)
// FAIL : MAUVAIS : Énoncer l'évidence
// Incrémenter le compteur de 1
count++
// Définir le nom sur le nom de l'utilisateur
name = user.name
JSDoc pour les APIs Publiques
/**
* Recherche des marchés par similarité sémantique.
*
* @param query - Requête de recherche en langage naturel
* @param limit - Nombre maximum de résultats (défaut : 10)
* @returns Tableau de marchés triés par score de similarité
* @throws {Error} Si l'API OpenAI échoue ou si Redis est indisponible
*
* @example
* ```typescript
* const results = await searchMarkets('election', 5)
* console.log(results[0].name) // "Trump vs Biden"
* ```
*/
export async function searchMarkets(
query: string,
limit: number = 10
): Promise<Market[]> {
// Implémentation
}
Bonnes Pratiques de Performance
Mémoïsation
import { useMemo, useCallback } from 'react'
// PASS : BON : Mémoïser les calculs coûteux
// Copier avant de trier - Array.prototype.sort mute sur place
const sortedMarkets = useMemo(() => {
return [...markets].sort((a, b) => b.volume - a.volume)
}, [markets])
// PASS : BON : Mémoïser les callbacks
const handleSearch = useCallback((query: string) => {
setSearchQuery(query)
}, [])
Chargement Paresseux (Lazy Loading)
import { lazy, Suspense } from 'react'
// PASS : BON : Charger paresseusement les composants lourds
const HeavyChart = lazy(() => import('./HeavyChart'))
export function Dashboard() {
return (
<Suspense fallback={<Spinner />}>
<HeavyChart />
</Suspense>
)
}
Requêtes de Base de Données
// PASS : BON : Sélectionner uniquement les colonnes nécessaires
const { data } = await supabase
.from('markets')
.select('id, name, status')
.limit(10)
// FAIL : MAUVAIS : Tout sélectionner
const { data } = await supabase
.from('markets')
.select('*')
Standards de Test
Structure de Test (Motif AAA)
test('calcule la similarité correctement', () => {
// Arrange (Disposer)
const vector1 = [1, 0, 0]
const vector2 = [0, 1, 0]
// Act (Agir)
const similarity = calculateCosineSimilarity(vector1, vector2)
// Assert (Affirmer)
expect(similarity).toBe(0)
})
Nommage des Tests
// PASS : BON : Noms de test descriptifs
test('retourne un tableau vide quand aucun marché ne correspond à la requête', () => { })
test('lève une erreur quand la clé API OpenAI est manquante', () => { })
test('utilise la recherche par sous-chaîne en secours quand Redis est indisponible', () => { })
// FAIL : MAUVAIS : Noms de test vagues
test('fonctionne', () => { })
test('test recherche', () => { })
Détection des Odeurs de Code
Surveillez ces anti-patrons :
1. Fonctions Longues
// FAIL : MAUVAIS : Fonction > 50 lignes
function processMarketData() {
// 100 lignes de code
}
// PASS : BON : Diviser en fonctions plus petites
function processMarketData() {
const validated = validateData()
const transformed = transformData(validated)
return saveData(transformed)
}
2. Imbrication Profonde
// FAIL : MAUVAIS : 5+ niveaux d'imbrication
if (user) {
if (user.isAdmin) {
if (market) {
if (market.isActive) {
if (hasPermission) {
// Faire quelque chose
}
}
}
}
}
// PASS : BON : Retours anticipés
if (!user) return
if (!user.isAdmin) return
if (!market) return
if (!market.isActive) return
if (!hasPermission) return
// Faire quelque chose
3. Nombres Magiques
// FAIL : MAUVAIS : Nombres non expliqués
if (retryCount > 3) { }
setTimeout(callback, 500)
// PASS : BON : Constantes nommées
const MAX_RETRIES = 3
const DEBOUNCE_DELAY_MS = 500
if (retryCount > MAX_RETRIES) { }
setTimeout(callback, DEBOUNCE_DELAY_MS)
Rappel : La qualité du code n'est pas négociable. Un code clair et maintenable permet un développement rapide et un refactoring en toute confiance.