Files
gravl/.planning/codebase/CONVENTIONS.md
T
2026-02-15 21:49:31 +01:00

8.4 KiB

Coding Conventions

Analysis Date: 2026-02-15

Naming Patterns

Files:

  • Frontend pages: PascalCase with .jsx extension (e.g., Dashboard.jsx, LoginPage.jsx, WorkoutPage.jsx)
  • Frontend components: PascalCase with .jsx extension (e.g., Icons.jsx)
  • Backend routes: index.js for main server file
  • CSS files: kebab-case or match component name (e.g., index.css, App.css)
  • Context files: Named with Context suffix (e.g., AuthContext.jsx)

Functions:

  • Async functions: verb + noun pattern (e.g., fetchProgram, fetchLogs, handleSubmit)
  • Event handlers: handle prefix (e.g., handleSubmit, handleSave, handleChange)
  • Helper/utility functions: descriptive names without prefixes (e.g., getCoachGreeting, getMuscleGroups, getWeekStart)
  • Hook usage: Standard React hooks (e.g., useState, useEffect, useContext)
  • Middleware functions: descriptive names (e.g., authMiddleware)

Variables:

  • State variables: camelCase (e.g., user, loading, program, selectedDay)
  • Constants (config): UPPER_SNAKE_CASE or camelCase (e.g., API_URL, JWT_SECRET, PORT)
  • Local variables: camelCase (e.g., dayOfWeek, todayWorkout, lastWeight)
  • Boolean variables: descriptive (e.g., loading, editing, warmupDone, completedWarmups)
  • IDs: numeric or snake_case from database (e.g., user_id, program_exercise_id, program_day_id)

Types:

  • Objects/interfaces: use descriptive structure without explicit types (e.g., { id, email, onboarding_complete })
  • Database records: snake_case field names from schema (e.g., password_hash, body_fat_pct, measured_at)

Code Style

Formatting:

  • No explicit linter/formatter detected in config
  • Indentation: 2 spaces (observed in code)
  • Line length: typically under 100 characters
  • Quotes: single quotes in most files, double quotes in some (inconsistent but not enforced)
  • Semicolons: inconsistently used (some files omit, some include)

Linting:

  • No ESLint, Prettier, or Biome config files detected
  • No type checking (no TypeScript or JSDoc type annotations)

Spacing:

  • Components separated by blank lines
  • Function logic blocks separated by comments
  • Import statements grouped: React/library imports first, then local imports

Import Organization

Order:

  1. React and core library imports (e.g., import React from 'react')
  2. External libraries (e.g., react-router-dom, Express packages)
  3. Local imports (context, pages, components)
  4. CSS/asset imports (e.g., import './index.css')

Path Aliases:

  • No path aliases configured (@/ style paths not used)
  • Relative imports used throughout (e.g., './context/AuthContext', '../components/Icons')
  • Relative paths: ../ for parent directory navigation in page imports

Examples:

// Frontend (AuthContext.jsx)
import { createContext, useContext, useState, useEffect } from 'react';
import { useAuth } from '../context/AuthContext';
import { Icon } from '../components/Icons';
import './App.css';

// Backend (index.js)
const express = require('express');
const { Pool } = require('pg');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

Error Handling

Patterns:

  • Frontend (React): Try-catch blocks in async functions, error state managed with useState

    try {
      const res = await fetch(`${API_URL}/auth/login`, { ... });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error);
      // Handle success
    } catch (err) {
      setError(err.message);
    }
    
  • Backend (Express): Try-catch blocks in route handlers with status code responses

    try {
      // Database query or logic
      res.json(result);
    } catch (err) {
      if (err.code === '23505') return res.status(400).json({ error: 'Email already exists' });
      console.error('Operation error:', err);
      res.status(500).json({ error: 'Server error' });
    }
    
  • Error Response Format: JSON with error key: { error: 'Human-readable message' }

  • Status Codes: 400 (validation/conflict), 401 (auth), 404 (not found), 500 (server error)

  • Empty error catches: Some empty catch blocks without logging (e.g., catch { logout(); } in AuthContext)

Logging

Framework: Native console.error() only

Patterns:

  • Error logging: console.error('Context + error:', err)
  • Backend operations logged: console.error('Register error:', err), console.error('Profile error:', err)
  • Frontend operations: minimal logging (mostly silent failures)
  • No structured logging or log levels (DEBUG, INFO, WARN)
  • Log format: descriptive label + colon + error object

Examples from code:

console.error('Failed to fetch program:', err);
console.error('Login error:', err);
console.error('Update profile error:', err);

Comments

When to Comment:

  • Section headers for major logical blocks (e.g., // Coach section, // Today's action, // Quick stats)
  • Data structure explanations (e.g., // Uppvärmningsövningar baserat på muskelgrupp)
  • Complex calculations or business logic
  • Not applied to simple conditionals or obvious code

JSDoc/TSDoc:

  • Not used - no type annotations or formal documentation
  • Inline comments rare and minimal

Examples:

// Mappa övningar till muskelgrupper
function getMuscleGroups(exercises) { ... }

// Beräkna progress
const completedExercises = exercises.filter(ex => { ... });

// Check if log exists for this set
const existing = await pool.query(...);

Function Design

Size:

  • Page/component functions: 40-250 lines (includes JSX)
  • Helper functions: 5-30 lines
  • Backend route handlers: 10-50 lines

Parameters:

  • Named parameters for component props: { children, requireOnboarding = true }
  • Function parameters: individual arguments or destructured objects
  • Query parameters: destructured from request (e.g., const { user_id, date } = req.query)

Return Values:

  • React components return JSX directly
  • Async functions return Promise<JSON | null>
  • Helper functions return computed values or arrays
  • Route handlers return via res.json() or res.status().json()

Async/Await:

  • Preferred over .then() chains
  • Used consistently in all async operations
  • Combined with try-catch for error handling

Examples:

const fetchProgram = async () => {
  if (program) return; // Early return
  try {
    const res = await fetch(`${API_URL}/programs/1`);
    const data = await res.json();
    setProgram(data);
  } catch (err) {
    console.error('Failed to fetch program:', err);
  }
};

// Helper function
function isSameDay(d1, d2) {
  return d1.getDate() === d2.getDate() &&
         d1.getMonth() === d2.getMonth() &&
         d1.getFullYear() === d2.getFullYear();
}

Module Design

Exports:

  • Frontend: Default exports for pages/contexts: export default Dashboard
  • Frontend: Named exports for utilities: export const useAuth = () => useContext(AuthContext)
  • Backend: Direct route handlers with app.get(), app.post() etc. (not module exports)
  • Contexts: export function AuthProvider + export const useAuth

Barrel Files:

  • Icons component (Icons.jsx) exports multiple icon definitions and helper functions
  • Most modules single-responsibility (one component/context per file)

Examples:

// Context export pattern
export function AuthProvider({ children }) { ... }
export const useAuth = () => useContext(AuthContext);

// Component export pattern
export default function LoginPage() { ... }

// Backend (no module export pattern, direct app routing)
app.post('/api/auth/login', async (req, res) => { ... });

State Management

Frontend:

  • React useState hooks for local component state
  • React Context API for global auth state (AuthContext.jsx)
  • Parent component state passed down as props (e.g., App.jsx manages view, program, logs)
  • No Redux, Zustand, or Jotai

Backend:

  • In-memory database connections via Pool (pg package)
  • No state persistence between requests
  • Request-scoped data via middleware (e.g., req.user from JWT)

CSS/Styling

Approach: Plain CSS with CSS variables

  • CSS variables defined in :root: --bg-primary, --text-primary, --accent, etc.
  • Dark theme with fitness-oriented color palette
  • Classes: descriptive kebab-case (e.g., dashboard-header, calendar-day, page-main)
  • Utility/modifier classes: .active, .today, .has-workout, .loading
  • No CSS-in-JS or utility framework (no Tailwind, Styled Components)

Convention analysis: 2026-02-15