Coding Standards
This document defines the coding standards for DiveSuite. Consistency makes the codebase easier to read, review, and maintain.
TypeScript
Section titled “TypeScript”Configuration
Section titled “Configuration”TypeScript is configured with strict mode:
{ "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "noUnusedLocals": true, "noUnusedParameters": true }}Type Safety
Section titled “Type Safety”// Good: explicit typesfunction calculateNDL(depth: number, gasMix: GasMix): number { // ...}
// Bad: implicit anyfunction calculateNDL(depth, gasMix) { // ...}
// Good: use unknown for truly unknown typesfunction parseImport(data: unknown): DiveLog { // validate and parse}
// Bad: using anyfunction parseImport(data: any): DiveLog { // no type safety}Interfaces vs Types
Section titled “Interfaces vs Types”Prefer interface for object shapes:
// Preferred: interface for objectsinterface DiveLog { id: string; depth: number; duration: number;}
// Use type for unions, intersections, primitivestype DiveType = 'OC' | 'CCR' | 'SCR';type Meters = number;Exports
Section titled “Exports”Use named exports, not default exports:
// Good: named exportexport function DiveProfileChart() { ... }export interface DiveLog { ... }
// Bad: default exportexport default function DiveProfileChart() { ... }Error Handling
Section titled “Error Handling”Never swallow errors:
// Good: handle errors explicitlytry { const result = await planningService.calculate(input); return result;} catch (error) { logger.error('Planning calculation failed', { error, input }); throw new PlanningError('Calculation failed', { cause: error });}
// Bad: swallowing errorstry { return await planningService.calculate(input);} catch { return null; // Lost error information}Documentation
Section titled “Documentation”All public functions must have doc comments:
/// Calculates the no-decompression limit for the given parameters.////// # Arguments/// * `depth` - Depth in meters/// * `gas_mix` - The breathing gas mixture/// * `gf_high` - Gradient factor high (0-100)////// # Returns/// NDL in minutes, or 0 if already in decompression////// # Example/// ```/// let ndl = calculate_ndl(30.0, &GasMix::air(), 85);/// assert!(ndl > 0);/// ```pub fn calculate_ndl(depth: f64, gas_mix: &GasMix, gf_high: u8) -> u32 { // ...}Error Handling
Section titled “Error Handling”No panics in production code:
// Good: return Resultpub fn calculate_mod(gas_mix: &GasMix, pp_o2_limit: f64) -> Result<f64, DecoError> { if gas_mix.o2_fraction <= 0.0 { return Err(DecoError::InvalidGasMix("O2 fraction must be positive")); } Ok((pp_o2_limit / gas_mix.o2_fraction - 1.0) * 10.0)}
// Bad: panickingpub fn calculate_mod(gas_mix: &GasMix, pp_o2_limit: f64) -> f64 { assert!(gas_mix.o2_fraction > 0.0); // Will panic! (pp_o2_limit / gas_mix.o2_fraction - 1.0) * 10.0}Testing
Section titled “Testing”Every function needs at least one test:
#[cfg(test)]mod tests { use super::*;
#[test] fn test_calculate_ndl_air_18m() { let gas = GasMix::air(); let ndl = calculate_ndl(18.0, &gas, 85).unwrap(); assert_eq!(ndl, 56); // Expected value from reference }
#[test] fn test_calculate_ndl_zero_depth() { let gas = GasMix::air(); let ndl = calculate_ndl(0.0, &gas, 85).unwrap(); assert_eq!(ndl, 999); // Unlimited at surface }}File Size Limits
Section titled “File Size Limits”| File Type | Max Lines | Action When Exceeded |
|---|---|---|
| Screen component | 250 | Extract sub-components |
| UI component | 150 | Extract logic into hooks |
| Custom hook | 100 | Split into smaller hooks |
| Service class | 300 | Split by responsibility |
| Utility module | 200 | Group by domain |
| Rust module | 400 | Split into sub-modules |
| Test file | 500 | Split by test category |
| Type definitions | 200 | Split by domain |
Code Quality Limits
Section titled “Code Quality Limits”| Metric | Limit | Rationale |
|---|---|---|
| Line length | 100 chars | Readable without horizontal scroll |
| Function body | 30 lines | Fits on one screen |
| Function parameters | 4 max | Use options object beyond 4 |
| Nesting depth | 3 levels | Extract helper or return early |
| Imports per file | 15 max | Sign of too many dependencies |
Example: Reducing Parameters
Section titled “Example: Reducing Parameters”// Bad: too many parametersfunction createDivePlan( depth: number, duration: number, gasMix: GasMix, gfLow: number, gfHigh: number, altitude: number, ascentRate: number): DivePlan { ... }
// Good: options objectinterface PlanOptions { depth: number; duration: number; gasMix: GasMix; gradientFactors: { low: number; high: number }; altitude?: number; ascentRate?: number;}
function createDivePlan(options: PlanOptions): DivePlan { ... }Naming Conventions
Section titled “Naming Conventions”| Element | Convention | Example |
|---|---|---|
| Files (TS) | kebab-case.ts | dive-plan-service.ts |
| Files (Components) | PascalCase.tsx | DiveProfileChart.tsx |
| Files (Hooks) | useCamelCase.ts | useDivePlan.ts |
| Interfaces | PascalCase | DiveLog |
| Types | PascalCase | DiveType |
| Functions | camelCase | calculateNDL |
| Constants | UPPER_SNAKE_CASE | MAX_DEPTH_METERS |
| Database tables | snake_case | dive_log |
| i18n keys | namespace.section.key | planning.ndl.title |
| Rust modules | snake_case | buhlmann.rs |
| Rust functions | snake_case | calculate_ndl |
| Rust types | PascalCase | GasMix |
All user-facing strings must use the t() function:
// Good: i18nconst { t } = useTranslation();return <Text>{t('planning.ndl.result', { minutes: ndl })}</Text>;
// Bad: hardcoded stringreturn <Text>NDL: {ndl} minutes</Text>;Git Commits
Section titled “Git Commits”Use Conventional Commits:
feat: add altitude diving calculationfix: correct NDL at 30m with EAN32refactor: extract gas calculation utilitiestest: add property-based tests for tissue loadingdocs: update deco engine validation specchore: upgrade Expo to SDK 52Commit Message Structure
Section titled “Commit Message Structure”<type>(<scope>): <subject>
<body>
<footer>Example:
feat(planning): add reverse planning calculation
Implements P1-F08 (reverse planning) - given tank size and gas mix,calculate maximum bottom time at depth.
Closes #42