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