Skip to main content

Coding Conventions

This page documents the coding standards and patterns used across all services in The Shift Platform.

TypeScript

  • Target: ESNext
  • Module: nodenext
  • Strict mode: Always enabled
  • Runtime: Bun (with @types/bun for type checking)
  • No CommonJS: All packages use "type": "module" in package.json

Formatting and Linting

The platform uses Biome for both formatting and linting:

  • Quotes: Double quotes (")
  • Indentation: 2 spaces
  • Rules: Biome recommended rules
  • Config: biome.json at the platform root; submodules have their own identical copy

Run before committing:

bun run check    # Lint + format check
bun run format # Auto-format

Testing

  • Framework: bun:test
  • File naming: *.test.ts alongside the source file they test
  • Location: Tests live next to the code they test, not in a separate directory
bun run test              # All tests
cd yellowpages && bun test # Single service

CLI Conventions

All CLI commands use Commander.js and follow these patterns:

Global Flags

Every command supports:

  • --json -- Output structured JSON for agent consumption
  • --quiet -- Suppress all stdout, exit code only

Command Pattern

command
.argument("<id>", "Service ID or name")
.option("--json", "Output as JSON")
.option("--quiet", "Suppress output")
.action(async (id, options) => {
// 1. Validate input
// 2. requireRoot() — ensure store directory exists
// 3. Load data
// 4. Operate
// 5. output(result, options) — handles json/quiet/human modes
});

Exit Codes

Use semantic exit codes from @shift/platform-core/exit-codes:

CodeConstantMeaning
0EXIT_SUCCESSOperation completed
1EXIT_ERRORGeneral error
2EXIT_USER_ERRORInvalid input
3EXIT_NOT_INITIALIZEDStore directory missing
4EXIT_NOT_FOUNDResource not found
5EXIT_CONNECTIONServer unreachable

API Conventions

All HTTP APIs use Hono and follow these patterns:

Factory Pattern

Each service exports a createApp() function that returns a Hono app:

import { Hono } from "hono";
import { errorHandler } from "@shift/platform-core/api/error-handler";
import { healthRoute } from "@shift/platform-core/api/health";

export function createApp() {
const app = new Hono();
app.use("*", errorHandler());
app.route("/healthz", healthRoute("my-service"));
// ... routes
return app;
}

Response Envelope

All responses use the standard envelope from @shift/platform-core/api/response:

import { ok, err } from "@shift/platform-core/api/response";

// Success
return c.json(ok(data));
// { "success": true, "data": { ... } }

// Error
return c.json(err("NOT_FOUND", "Service not found"), 404);
// { "success": false, "error": { "code": "NOT_FOUND", "message": "Service not found" } }

Error Handling

Use error helpers from @shift/platform-core/api/error-handler:

import { badRequest, notFound, conflict } from "@shift/platform-core/api/error-handler";

if (!name) throw badRequest("Name is required");
if (!service) throw notFound("Service not found");
if (existing) throw conflict("Service already exists");

ID Conventions

  • Generation: nanoid(8) for all application-level IDs
  • Resolution: All commands and endpoints accept either an ID or a human-readable name
  • Convex bridging: App uses id, Convex uses _id, and every table has a sid field (shift ID) indexed for fast lookup

Storage Conventions

Services define store interfaces in packages/shared/src/repository.ts and provide implementations for each backend:

packages/shared/src/
repository.ts # Store interface
store/
file.ts # File-based implementation
convex.ts # Convex implementation
factory.ts # createStore() factory based on SHIFT_STORAGE

The factory reads SHIFT_STORAGE from the environment and returns the appropriate implementation.

Output Triple

Every CLI command supports three output modes via the output() helper from @shift/platform-core/output:

  1. Human (default): Chalk-colored, formatted for terminals
  2. JSON (--json): Structured output for agent consumption
  3. Quiet (--quiet): No stdout, exit codes only