# Coding Conventions **Analysis Date:** 2026-03-11 ## Naming Patterns **Files:** - React components: PascalCase (e.g., `AppLayout.tsx`, `LoginPage.tsx`) - TypeScript utilities/libraries: camelCase (e.g., `useAuth.ts`, `api.ts`, `utils.ts`) - Go packages: lowercase, no underscore (e.g., `auth`, `db`, `models`, `api`) - Go files: descriptive lowercase (e.g., `handlers.go`, `queries.go`, `auth.go`) - Test files: None currently present (testing infrastructure not yet established) **Functions:** - React hooks: `use` prefix in camelCase (e.g., `useAuth()`, `useBudgets()`) - Go receiver methods: ExportedName style on receiver (e.g., `(h *Handlers) Register`) - Go internal functions: camelCase for unexported, PascalCase for exported (e.g., `writeJSON()`, `ListCategories()`) - TypeScript functions: camelCase (e.g., `request()`, `cn()`) **Variables:** - Local variables: camelCase (e.g., `userID`, `displayName`, `carryover`) - Constants: UPPER_SNAKE_CASE in Go (e.g., `CategoryBill`, `userIDKey`), camelCase in TypeScript (e.g., `API_BASE`) - React state: camelCase for state and setter (e.g., `[user, setUser]`, `[loading, setLoading]`) **Types:** - TypeScript interfaces: PascalCase, no `I` prefix (e.g., `User`, `Category`, `Budget`, `BudgetItem`) - TypeScript type aliases: PascalCase (e.g., `CategoryType` = 'bill' | 'variable_expense' | ...) - Go structs: PascalCase (e.g., `User`, `Category`, `Budget`) - Go type enums: PascalCase with prefixed constants (e.g., `CategoryType`, `CategoryBill`, `CategoryIncome`) ## Code Style **Formatting:** - TypeScript: Vite default (2-space indentation implied by no explicit prettier config) - Go: `gofmt` standard (1-tab indentation) - Language-specific linters enforced via `npm run lint` (ESLint) and `go vet` **Linting:** - Frontend: ESLint with TypeScript support (`eslint.config.js`) - Extends: `@eslint/js`, `typescript-eslint`, `eslint-plugin-react-hooks`, `eslint-plugin-react-refresh` - Targets: All `.ts` and `.tsx` files - Rules enforced: React hooks rules, React refresh rules - Go: Standard `go vet` (no additional linter configured) **TypeScript Compiler Settings:** - `strict: true` - Full strict type checking enabled - `noUnusedLocals: true` - Unused variables are errors - `noUnusedParameters: true` - Unused parameters are errors - `noFallthroughCasesInSwitch: true` - Switch cases must explicitly break or return - `noUncheckedSideEffectImports: true` - Side-effect imports must be explicit - Target: ES2022, Module: ESNext ## Import Organization **TypeScript Order:** 1. Third-party React/UI imports (e.g., `from 'react'`, `from 'react-router-dom'`) 2. Component/utility imports from `@/` alias 3. Type imports (e.g., `type User`) 4. CSS/global imports (e.g., `'@/i18n'`) Example from `src/components/AppLayout.tsx`: ```typescript import { useTranslation } from 'react-i18next' import { Link, useLocation } from 'react-router-dom' import { LayoutDashboard, Tags, Settings, LogOut } from 'lucide-react' import { Sidebar, ... } from '@/components/ui/sidebar' import { Button } from '@/components/ui/button' import type { AuthContext } from '@/hooks/useAuth' ``` **Go Import Order:** 1. Standard library imports (e.g., `"context"`, `"encoding/json"`) 2. Third-party imports (e.g., `"github.com/go-chi/chi/v5"`) 3. Internal imports (e.g., `"simplefinancedash/backend/internal/..."`) Example from `backend/internal/api/handlers.go`: ```go import ( "encoding/json" "net/http" "time" "github.com/go-chi/chi/v5" "github.com/google/uuid" "simplefinancedash/backend/internal/auth" ) ``` **Path Aliases:** - TypeScript: `@/*` maps to `./src/*` (defined in `tsconfig.json`) - Go: Full module paths from module name `simplefinancedash/backend` ## Error Handling **TypeScript Patterns:** - Silent error suppression with fallback (preferred for non-critical operations): ```typescript try { const u = await auth.me() setUser(u) } catch { setUser(null) // Fallback without logging } ``` - Custom error class for API errors: ```typescript export class ApiError extends Error { status: number constructor(status: number, message: string) { super(message) this.name = 'ApiError' this.status = status } } ``` - HTTP response error handling in `request()`: ```typescript if (!res.ok) { const body = await res.json().catch(() => ({})) throw new ApiError(res.status, body.error || res.statusText) } ``` **Go Patterns:** - Wrap errors with context using `fmt.Errorf("context: %w", err)` for traceability: ```go hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return "", fmt.Errorf("hashing password: %w", err) } ``` - Return early on error in handlers: ```go if err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } ``` - Silent errors in non-critical flows with zero-value fallback: ```go var exists bool err := pool.QueryRow(...).Scan(&exists) if err != nil { exists = false // Assume false on error } ``` ## Logging **Framework:** No structured logging library. Uses `log` package in Go, `console` in TypeScript. **Patterns:** - Go: `log.Printf()` for informational messages (e.g., "Server starting on :8080"), `log.Fatalf()` for fatal errors during startup - Located in `backend/cmd/server/main.go` - TypeScript: No logging in production code. Errors silently caught and handled with UI fallbacks. ## Comments **When to Comment:** - Go: Comments for exported types/functions (standard Go convention) - Comment each exported function above its definition - Used in `backend/internal/auth/auth.go` for public auth functions - TypeScript: Minimal comments; code clarity preferred - JSDoc comments not currently used - Comments only for non-obvious logic or integration details **Current Usage:** - Go handler comments: "// Auth Handlers", "// Helpers" as section dividers in `api/handlers.go` - TypeScript code: Inline comments absent (code is self-documenting with clear naming) ## Function Design **Size:** - Go handler functions: 10-50 lines (moderate size with early returns for validation) - Go query functions: Variable (from 5 lines for simple queries to 30+ for complex operations like `GetBudgetWithItems()`) - TypeScript hooks: 20-40 lines per hook (following React patterns with useState/useEffect) **Parameters:** - Go: Receiver pattern for struct methods, context as first parameter in database functions - Example: `func (q *Queries) CreateUser(ctx context.Context, email, passwordHash, displayName, locale string) (*models.User, error)` - TypeScript: Props as single typed object, destructured in function signature - Example: `export function AppLayout({ auth, children }: Props)` - React hooks: No parameters passed to hooks; they use internal state **Return Values:** - Go: Multiple returns with error as last return (e.g., `(*models.User, error)`) - TypeScript: Single return object containing multiple pieces of state/functions - Example: `useBudgets()` returns `{ list, current, loading, fetchList, selectBudget, setCurrent }` ## Module Design **Exports:** - Go: Exported types/functions start with capital letter (PascalCase) - Non-exported functions use camelCase - Pattern: Define helpers after main exports in same file - TypeScript: Named exports for components/utilities, default exports for page components - Example page export: `export default function App()` - Example utility export: `export class ApiError extends Error` - Example hook export: `export function useAuth()` **Barrel Files:** - No barrel files currently used (no `index.ts` re-exports in `src/lib`, `src/hooks`, etc.) - Each utility/hook imported directly from its file - Components from shadcn/ui imported from `@/components/ui/[component-name]` ## API Client Pattern **Location:** `frontend/src/lib/api.ts` **Structure:** ```typescript // 1. Helper function for all requests async function request(path: string, options?: RequestInit): Promise { ... } // 2. Custom error class export class ApiError extends Error { ... } // 3. Type definitions export interface User { ... } export interface Category { ... } // 4. Namespace objects for endpoint groups export const auth = { register: (...) => request(...), login: (...) => request(...), ... } export const categories = { list: () => request(...), create: (...) => request(...), ... } ``` This pattern provides: - Type safety with generic `request` - Automatic error handling and JSON parsing - Organized endpoint grouping by resource - Consistent authentication (cookies via `credentials: 'include'`) ## React Component Pattern **Props Pattern:** - Define interface above component: `interface Props { ... }` - Destructure in function signature: `export function Component({ prop1, prop2 }: Props)` - Type children explicitly: `children: React.ReactNode` Example from `AppLayout.tsx`: ```typescript interface Props { auth: AuthContext children: React.ReactNode } export function AppLayout({ auth, children }: Props) { ... } ``` **Hook Usage:** - Always call hooks at top level of component - Use `useCallback` for memoized functions passed to child components - Combine related state with custom hooks (e.g., `useAuth()`, `useBudgets()`) --- *Convention analysis: 2026-03-11*