Skip to main content

Coding Conventions

Development standards for the CROW codebase. These rules ensure consistency, readability, and maintainability across all services.

Philosophy

Optimize for clarity over cleverness. Code should be self-explanatory, flat, and predictable. If you need a comment to explain what code does, the code needs to be rewritten.

Rules Summary

  1. No comments in production code
  2. Max 1 level of indentation
  3. FP first, avoid mutation
  4. 20-40 line functions
  5. Long descriptive names
  6. TypeScript strict mode, zero errors
  7. as never for OpenAPI route type narrowing

TypeScript Standards

1. No Comments

Code must be self-documenting. Comments are a code smell.

Instead of commenting, use descriptive variable and function names, extract complex logic into well-named functions, use guard clauses and early returns.

Exception: Public API documentation for exported interfaces consumed by external parties.

// BAD
function calc(x: number, y: number): number {
// Calculate the discount based on order total
const d = x > 100 ? 0.2 : 0.1
return y - (y * d)
}

// GOOD
function calculateDiscountedPrice(orderTotal: number, itemPrice: number): number {
const discountRate = orderTotal > 100 ? 0.2 : 0.1
const discountAmount = itemPrice * discountRate
return itemPrice - discountAmount
}

2. Maximum 1 Level of Indentation

Functions must not exceed 1 level of indentation (excluding the function body itself). Deep nesting multiplies cognitive load.

Techniques: guard clauses, early returns, array methods (map, filter, flatMap, reduce), lookup tables.

// BAD
function processUser(user: User | null): string {
if (user) {
if (user.isActive) {
if (user.hasPermission('admin')) {
return 'Admin user processed'
}
}
}
return 'No user found'
}

// GOOD
function processUser(user: User | null): string {
if (!user) return 'No user found'
if (!user.isActive) return 'Inactive user'
if (user.hasPermission('admin')) return 'Admin user processed'
return 'Regular user processed'
}

3. Functional Programming First

Default to FP patterns. OOP is a last resort.

Prefer pure functions, immutable data structures, function composition, data transformations over mutation.

Use OOP only for Durable Objects (required by Cloudflare), managing complex lifecycle/state, or implementing required library interfaces.

4. Function Size: 20-40 Lines Maximum

Small functions are easier to understand, test, and maintain. If a function is longer, it has multiple responsibilities. Extract helper functions and apply single responsibility.

5. Long Descriptive Names

Optimize for reading, not typing.

  • Use full words, no abbreviations
  • Booleans: prefix with is, has, should, can
  • Functions: verb prefixes (get, fetch, create, update, delete, calculate, transform, validate)
  • Avoid vague names: data, info, manager, util, handler

6. TypeScript Strict Mode

All services use strict TypeScript compilation with zero errors. Enable strict mode in tsconfig.json.

7. as never for OpenAPI Route Type Narrowing

When using @hono/zod-openapi, early return responses (error paths) may not match the route's success type. Use as never to satisfy the type checker:

app.openapi(GetOrganizationRoute, async c => {
const callerOrgId = c.req.header('X-Organization-Id')
if (!callerOrgId || callerOrgId !== id) {
return c.json({ error: 'Forbidden' }, 403) as never
}
// ... success path
})

This is the standard pattern across all CROW services for BOLA checks and validation errors in OpenAPI routes.

Architecture Patterns

Layer-Based Organization

src/
config/ # Configuration and validation
db/ # Schema and migrations
middleware/ # Hono middleware (auth, jwt, cors)
routes/ # Route definitions (OpenAPI schemas)
services/ # Business logic
utils/ # Shared utilities (formatters, error handlers)
types.ts # Type definitions and Environment interface
index.ts # App entrypoint and route registration

Error Handling

  • ZodError details are never leaked to clients (sanitized to generic "Invalid request parameters")
  • Errors are logged once at the boundary
  • handleErrorResponse utility provides consistent error formatting

Validation

  • Request validation via Zod schemas defined in OpenAPI routes
  • Email validation: RFC 5321 (max 254 chars), HTML tag rejection in names
  • URL validation: SSRF protection blocks private/loopback/link-local ranges and internal CROW hostnames

Commit Messages

Conventional Commits via commitlint:

<type>: <subject>

Types: feat, fix, docs, style, refactor, test, chore

Enforcement

  • Code reviews (all PRs require approval)
  • ESLint (@antfu/eslint-config)
  • Prettier for formatting
  • Pre-commit hooks via Husky + lint-staged
  • CI pipeline checks