Skip to main content

Code Style Philosophy

Autonome enforces strict code style to maintain consistency and quality across the codebase:
  • Zero tech debt: Do things right from the start, no workarounds
  • Adaptive refactoring: Clean up sloppy code whenever you encounter it
  • No bandaid fixes: Fix issues at their source, not with patches
  • Complete implementations: Build for scale (>1000 users), not quick hacks
From AGENTS.md: “Early development, no users. No backwards compatibility concerns. Do things RIGHT: clean, organized, zero tech debt. Never create compatibility shims.”

Biome Configuration

Autonome uses Biome for linting and formatting instead of ESLint + Prettier:
  • Faster than ESLint (10-100x speedup)
  • Single tool for both linting and formatting
  • Zero config required (sensible defaults)
  • Native to the JavaScript ecosystem

Configuration File

Biome is configured in biome.json:
{
  "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
  "formatter": {
    "enabled": true,
    "indentStyle": "tab"
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "double"
    }
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true
    }
  },
  "assist": {
    "actions": {
      "source": {
        "organizeImports": "on"
      }
    }
  }
}

Running Biome

# Check for issues (lint + format)
bun run check

# Format code
bun run format

# Lint code
bun run lint

# Auto-fix issues
bun run check --write
Run bun run check before committing to catch style violations early.

Core Style Rules

1. Indentation: Tabs

Always use tabs for indentation, never spaces:
// Good ✅
function calculatePnl(position: Position) {
	const { side, quantity, averagePrice, currentPrice } = position;
	if (side === "BUY") {
		return (currentPrice - averagePrice) * quantity;
	}
	return (averagePrice - currentPrice) * quantity;
}

// Bad ❌ (spaces)
function calculatePnl(position: Position) {
  const { side, quantity, averagePrice, currentPrice } = position;
  if (side === "BUY") {
    return (currentPrice - averagePrice) * quantity;
  }
  return (averagePrice - currentPrice) * quantity;
}
Why tabs?
  • Accessibility: Users can configure tab width to their preference
  • Smaller file sizes
  • Easier to navigate with keyboard

2. Quotes: Double

Always use double quotes for strings:
// Good ✅
const message = "Hello, world!";
const symbol = "BTC-USD";
import { Button } from "@/components/ui/button";

// Bad ❌
const message = 'Hello, world!';
const symbol = 'BTC-USD';
import { Button } from '@/components/ui/button';
Exception: Use backticks for template literals:
const greeting = `Hello, ${name}!`;
const query = `
	SELECT * FROM "Orders"
	WHERE "status" = 'OPEN'
`;

3. Import Organization

Biome automatically organizes imports:
// Correct order (automatic):
// 1. External packages
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import * as Sentry from "@sentry/react";

// 2. Internal absolute imports
import { orpc } from "@/server/orpc/client";
import { Button } from "@/components/ui/button";
import { calculatePnl } from "@/core/shared/trading/calculations";

// 3. Relative imports
import { PositionCard } from "./PositionCard";
import { usePositions } from "../hooks/usePositions";

// 4. Type imports (grouped separately)
import type { Position } from "@/db/schema";
import type { ReactNode } from "react";

4. Semicolons

Biome enforces semicolons at the end of statements:
// Good ✅
const x = 5;
return calculatePnl(position);

// Bad ❌
const x = 5
return calculatePnl(position)

TypeScript Conventions

Type Annotations

Use explicit types for function parameters and return values:
// Good ✅
function calculatePnl(position: Position): number {
	const pnl = (position.currentPrice - position.averagePrice) * position.quantity;
	return pnl;
}

// Acceptable (return type inferred)
function getSymbol(position: Position) {
	return position.symbol; // string inferred
}

// Bad ❌ (no parameter types)
function calculatePnl(position) {
	return position.currentPrice - position.averagePrice;
}

Type vs Interface

Prefer type over interface for most cases:
// Good ✅
type Position = {
	id: string;
	side: "BUY" | "SELL";
	quantity: string;
	averagePrice: string;
};

// Use interface for extending
interface ExtendedPosition extends Position {
	exitPlan?: ExitPlan;
}

Avoid any

Never use any - use unknown or proper types:
// Good ✅
function parseJson(data: string): unknown {
	return JSON.parse(data);
}

const result = parseJson(jsonString);
if (typeof result === "object" && result !== null) {
	// Safe to use
}

// Bad ❌
function parseJson(data: string): any {
	return JSON.parse(data);
}

React Component Patterns

Component Structure

import type { ReactNode } from "react";
import { cn } from "@/lib/utils";
import { cva, type VariantProps } from "class-variance-authority";

// 1. Variants definition (if applicable)
const buttonVariants = cva(
	"inline-flex items-center justify-center rounded-md font-medium",
	{
		variants: {
			variant: {
				default: "bg-primary text-primary-foreground",
				destructive: "bg-destructive text-destructive-foreground",
				outline: "border border-input",
			},
			size: {
				default: "h-10 px-4",
				sm: "h-9 px-3",
				lg: "h-11 px-8",
			},
		},
		defaultVariants: {
			variant: "default",
			size: "default",
		},
	},
);

// 2. Props interface
interface ButtonProps
	extends React.ButtonHTMLAttributes<HTMLButtonElement>,
		VariantProps<typeof buttonVariants> {
	children: ReactNode;
}

// 3. Component definition
export function Button({ className, variant, size, ...props }: ButtonProps) {
	return (
		<button
			className={cn(buttonVariants({ variant, size, className }))}
			{...props}
		/>
	);
}

Hooks Conventions

Custom hooks must start with use:
// Good ✅
export function usePositions(modelId: string) {
	return useQuery(
		orpc.trading.getPositions.queryOptions({
			input: { modelId },
		}),
	);
}

// Bad ❌
export function getPositions(modelId: string) {
	// Not a hook name
}

Event Handlers

Prefix event handlers with handle:
function PositionCard({ position }: Props) {
	const handleClose = () => {
		// Close position logic
	};

	const handleEdit = () => {
		// Edit logic
	};

	return (
		<div>
			<Button onClick={handleClose}>Close</Button>
			<Button onClick={handleEdit}>Edit</Button>
		</div>
	);
}

Naming Conventions

Variables & Functions

camelCase for variables and functions:
const portfolioValue = 10000;
const currentPrice = 50.25;

function calculateUnrealizedPnl() {}
function getOpenPositions() {}

Constants

UPPER_SNAKE_CASE for constants:
const INITIAL_CAPITAL = 10000;
const MAX_POSITION_SIZE = 1000;
const API_BASE_URL = "https://api.example.com";

// Exception: Configuration objects use camelCase
const VARIANT_CONFIG = {
	Apex: { color: "#FF6B6B", label: "Apex" },
	Trendsurfer: { color: "#4ECDC4", label: "Trendsurfer" },
};

Types & Interfaces

PascalCase for types, interfaces, and enums:
type Position = {
	id: string;
	side: OrderSide;
};

interface PositionCardProps {
	position: Position;
	onClose: () => void;
}

enum OrderStatus {
	OPEN = "OPEN",
	CLOSED = "CLOSED",
	CANCELED = "CANCELED",
}

Files & Directories

kebab-case for file names (except components):
src/
├── core/
│   └── shared/
│       └── trading/
│           ├── calculations.ts        # kebab-case
│           └── fill-tracker.ts        # kebab-case
├── components/
│   └── ui/
│       ├── Button.tsx                 # PascalCase for components
│       └── PositionCard.tsx           # PascalCase for components
└── server/
    └── features/
        └── trading/
            └── trading-repository.ts  # kebab-case

Database Conventions

Quoted Identifiers

Always quote capitalized table and column names:
// Good ✅
const positions = await db
	.select()
	.from(schema.Orders)
	.where(eq(schema.Orders.status, "OPEN"));

// SQL: SELECT * FROM "Orders" WHERE "status" = 'OPEN'

// Bad ❌
const positions = await db
	.select()
	.from(Orders) // Unquoted - will fail in PostgreSQL
	.where(eq(Orders.status, "OPEN"));

Monetary Values

Store as TEXT, cast to NUMERIC for calculations:
// Schema definition
export const Orders = pgTable("Orders", {
	id: text("id").primaryKey(),
	averagePrice: text("averagePrice").notNull(),
	realizedPnl: text("realizedPnl"),
});

// SQL queries
const totalPnl = await db.execute(sql`
	SELECT SUM(CAST("realizedPnl" AS NUMERIC)) as total
	FROM "Orders"
	WHERE "status" = 'CLOSED'
`);

oRPC Patterns

Procedure Structure

import "@/polyfill"; // Always import at top
import { os } from "@orpc/server";
import { z } from "zod";
import * as Sentry from "@sentry/react";

export const getPositions = os
	.input(
		z.object({
			modelId: z.string().optional(),
			variant: z.enum(["Apex", "Trendsurfer", "Contrarian", "Sovereign"]).optional(),
		}),
	)
	.output(
		z.array(
			z.object({
				id: z.string(),
				side: z.enum(["BUY", "SELL"]),
				quantity: z.string(),
				averagePrice: z.string(),
			}),
		),
	)
	.handler(async ({ input }) =>
		Sentry.startSpan({ name: "getPositions" }, async () => {
			const { modelId, variant } = input;
			// Implementation
			return positions;
		}),
	);

Client Usage

import { useQuery } from "@tanstack/react-query";
import { orpc } from "@/server/orpc/client";

function Positions() {
	const { data: positions } = useQuery(
		orpc.trading.getPositions.queryOptions({
			input: { modelId: "model-123" },
		}),
	);

	return <div>{/* Render positions */}</div>;
}

Critical Rules from AGENTS.md

1. Package Manager: Bun Only

# Good ✅
bun install
bun add package-name
bun run dev

# Bad ❌
npm install
pnpm add package-name
yarn dev

2. Data Fetching: oRPC Only

// Good ✅
const { data } = useQuery(
	orpc.trading.getPositions.queryOptions({ input: {} }),
);

// Bad ❌
const response = await fetch("/api/positions");
const data = await response.json();

3. Environment Variables: T3Env

// Good ✅
import { env } from "@/env";

const apiKey = env.LIGHTER_API_KEY;
const dbUrl = env.DATABASE_URL;

// Bad ❌
const apiKey = process.env.LIGHTER_API_KEY;
const dbUrl = import.meta.env.DATABASE_URL;

4. No Workarounds

// Bad ❌ - Bandaid fix
function calculatePnl(position: any) {
	// @ts-ignore
	return position.currentPrice - position.entryPrice;
}

// Good ✅ - Proper fix
function calculatePnl(position: Position): number {
	if (!position.currentPrice || !position.averagePrice) {
		throw new Error("Missing price data");
	}
	return position.currentPrice - position.averagePrice;
}

5. Adaptive Refactoring

Before:
// Encountered sloppy code
function calc(p) {
	return p.cp - p.ep;
}
After:
// Refactored while working nearby
function calculatePnl(position: Position): number {
	const { currentPrice, averagePrice } = position;
	return currentPrice - averagePrice;
}

6. Edge Case Exhaustion

// Good ✅ - All edge cases handled
function calculateWinRate(trades: Trade[]): number {
	// Edge case: Empty array
	if (trades.length === 0) {
		return 0;
	}

	// Edge case: All trades are break-even
	const winningTrades = trades.filter((t) => t.pnl > 0);
	if (winningTrades.length === 0) {
		return 0;
	}

	// Normal case
	return winningTrades.length / trades.length;
}

Component Styling (Tailwind + CVA)

Using CVA for Variants

import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const badgeVariants = cva(
	"inline-flex items-center rounded-md px-2 py-1 text-xs font-semibold",
	{
		variants: {
			variant: {
				default: "bg-primary text-primary-foreground",
				success: "bg-green-500 text-white",
				error: "bg-red-500 text-white",
			},
		},
		defaultVariants: {
			variant: "default",
		},
	},
);

interface BadgeProps extends VariantProps<typeof badgeVariants> {
	children: React.ReactNode;
	className?: string;
}

export function Badge({ variant, className, children }: BadgeProps) {
	return (
		<span className={cn(badgeVariants({ variant }), className)}>
			{children}
		</span>
	);
}

Using cn() for Class Merging

import { cn } from "@/lib/utils";

// Merge classes with proper precedence
<div
	className={cn(
		"base-class p-4 bg-white",
		isActive && "bg-blue-500", // Conditional
		className, // User override
	)}
/>

VS Code Integration

Add to .vscode/settings.json:
{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  },
  "[typescript]": {
    "editor.defaultFormatter": "biomejs.biome"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "biomejs.biome"
  }
}

Pre-Commit Checklist

Before committing code:
  • Run bun run check (no errors)
  • Run bun run test (all tests pass)
  • Verify no console.log statements (unless intentional)
  • Check for unused imports/variables
  • Ensure proper TypeScript types (no any)
  • Verify database queries use quoted identifiers
  • Confirm environment variables use env.ts
  • Test affected functionality manually

Common Mistakes

Mistake 1: Using spaces instead of tabs

// Wrong
function foo() {
  return "bar"; // 2 spaces
}

// Correct
function foo() {
	return "bar"; // 1 tab
}
Fix: Run bun run format --write

Mistake 2: Single quotes

// Wrong
const message = 'Hello';

// Correct
const message = "Hello";
Fix: Run bun run format --write

Mistake 3: Unquoted database identifiers

// Wrong
await db.select().from(Orders);

// Correct
await db.select().from(schema.Orders); // Uses quoted "Orders"

Mistake 4: Direct process.env access

// Wrong
const apiKey = process.env.LIGHTER_API_KEY;

// Correct
import { env } from "@/env";
const apiKey = env.LIGHTER_API_KEY;

Mistake 5: Not organizing imports

// Wrong
import { Button } from "./Button";
import { useState } from "react";
import { env } from "@/env";

// Correct (Biome auto-organizes)
import { useState } from "react";
import { env } from "@/env";
import { Button } from "./Button";
Fix: Run bun run check --write

Next Steps

  • Set up your editor: Configure VS Code with Biome extension
  • Practice: Follow these conventions in your daily work
  • Review: Read Testing for QA workflow
  • Contribute: See Contributing Guidelines for the full development process
Consistency is more important than personal preference. Follow the style guide even if you disagree with specific choices.

Build docs developers (and LLMs) love