Skip to content

Latest commit

 

History

History
424 lines (330 loc) · 13.1 KB

File metadata and controls

424 lines (330 loc) · 13.1 KB
# Coding Standards This document defines the coding conventions and best practices for all implementations in the AI Product OS. All engineering agents must follow these standards when generating code. --- ## Language & Framework Standards ### TypeScript - **Required**: All new code must be written in TypeScript, not JavaScript - Use strict mode (`"strict": true` in tsconfig.json) - Avoid `any` types - use proper type definitions or `unknown` with type guards - Export types alongside implementations for reusability ### Next.js App Router - Use App Router (`app/` directory) over Pages Router for all new projects - Organize by feature: `app/[feature]/page.tsx`, `app/api/[resource]/route.ts` - Server Components by default, Client Components only when necessary (`"use client"`) - API Routes must use modern `NextResponse` and `NextRequest` types --- ## Code Organization ### File Structure ``` apps/[project-name]/ � src/ � � app/ # Next.js App Router � � � api/ # API routes � � � [feature]/ # Feature-based pages � � � layout.tsx # Root layout � � components/ # Reusable UI components � � lib/ # Utilities, clients, helpers � public/ # Static assets � schema.sql # Database schema (if applicable) � package.json � README.md ``` ### Naming Conventions - **Files**: `kebab-case.ts`, `PascalCase.tsx` (for components) - **Components**: `PascalCase` (e.g., `TaskBoard`, `PostHogProvider`) - **Functions**: `camelCase` (e.g., `fetchTasks`, `markDone`) - **Constants**: `SCREAMING_SNAKE_CASE` (e.g., `PM_CATEGORIES`, `API_TIMEOUT`) - **Types/Interfaces**: `PascalCase` (e.g., `Task`, `Category`) --- ## API Design ### Request Validation - **Always validate inputs**: Check for `null`, `undefined`, empty strings, type mismatches - **Enforce limits early**: Prevent abuse by validating length, count, size at the entry point - **Return descriptive errors**: Use 400 for client errors with clear messages ```typescript // GOOD if (!taskText || typeof taskText !== 'string' || taskText.trim().length === 0) { return NextResponse.json({ error: 'Task text is required' }, { status: 400 }); } if (taskText.length > 500) { return NextResponse.json({ error: 'Task text too long (max 500 chars)' }, { status: 400 }); } ``` ### Database Queries - **Always use `.limit()`**: Never fetch unbounded lists - **Index primary queries**: Ensure columns used in WHERE/ORDER BY are indexed - **Use batch operations**: Prefer `.in()` over N individual queries in loops ```typescript // GOOD - Limited query const { data } = await supabase .from('tasks') .select('*') .order('created_at', { ascending: false }) .limit(100); // BAD - Unbounded query const { data } = await supabase.from('tasks').select('*'); ``` --- ## Async & Promises ### Serverless Function Rules - **Never fire-and-forget**: All async operations in API routes MUST be `await`ed before returning - **No background promises**: Serverless environments (Vercel, AWS Lambda) suspend execution immediately after HTTP response ```typescript // GOOD await sendWhatsAppMessage(userId, message); return NextResponse.json({ success: true }); // BAD - Message will be dropped in production sendWhatsAppMessage(userId, message); // Fire-and-forget return NextResponse.json({ success: true }); ``` ### Concurrent Processing - Use `Promise.all()` or `Promise.allSettled()` for independent parallel operations - Avoid sequential `await` in loops when operations can run concurrently ```typescript // GOOD - Concurrent execution const results = await Promise.allSettled( users.map(user => processUser(user)) ); // BAD - Sequential execution (slow) for (const user of users) { await processUser(user); } ``` --- ## Error Handling ### External API Calls - **Classify errors**: Distinguish between transient (503, rate limit) and permanent (404, 401) - **Implement fallbacks**: Never lose user data due to third-party failures - **Log comprehensively**: Use structured logging with context ```typescript try { const result = JSON.parse(aiResponse.text); } catch (e) { console.error("Failed to parse AI JSON:", aiResponse.text); // Apply fallback to prevent data loss result = { category: 'ops', priority: 'medium', title: `[Review Needed] ${input.substring(0, 30)}...` }; } ``` ### AI Response Parsing - **Sanitize before parsing**: Strip markdown codeblocks from LLM outputs - **Validate structure**: Ensure required fields exist and match expected types - **Provide graceful degradation**: Save raw input if AI processing fails ```typescript // GOOD - Defensive parsing const cleanText = resultText.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim(); const result = JSON.parse(cleanText); if (!VALID_CATEGORIES.includes(result.category)) { console.error("Invalid AI categorization:", result); isFallback = true; } ``` --- ## Clipboard Operations **Never use a silent catch block on clipboard copy.** If the user triggers a copy action during a live workflow, silent failure is equivalent to a broken product. ```typescript // GOOD - Clipboard with fallback + error state async function copyToClipboard(text: string): Promise { try { await navigator.clipboard.writeText(text); return true; } catch { try { const textarea = document.createElement('textarea'); textarea.value = text; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); return true; } catch { return false; } } } // In component: show inline error ("Copy failed — please select manually") if copyToClipboard returns false ``` // Added: 2026-03-19 — SMB Feature Bundling Engine --- ## Performance & Limits ### Hard Constraints - **Input limits**: Max 500 characters for user text inputs - **Pagination**: Default page size of 100, never exceed 1000 - **API timeouts**: 10s for AI calls, 5s for database queries - **Cron duration**: Max 4 minutes for serverless cron jobs (stay under platform limits) ### Loop Constraints - **External API loops**: MUST have both a page limit AND a temporal bound - **Example**: `max 5 pages` AND `newer_than:30d` for Gmail syncing - **Failure limits**: Implement retry counters or dead-letter queues to prevent infinite poison-pill loops --- ## Security ### Secrets Management - **Never commit secrets**: Use `.env.local` for local dev, platform secrets for production - **Prefix public vars**: `NEXT_PUBLIC_*` for client-safe vars only - **Encrypt sensitive data**: Use AES-256-GCM for OAuth tokens, API keys in database ### Row Level Security (RLS) - **Enable on all user tables**: Supabase tables must have RLS policies - **Default deny**: Start with no access, explicitly grant permissions - **Use auth.uid()**: Tie policies to authenticated user ID --- ## Testing ### Manual Testing Checklist - **Happy path**: Standard valid input - **Edge cases**: Empty strings, zero values, maximum lengths - **Invalid inputs**: Wrong types, special characters, malformed JSON - **Network failures**: API timeouts, 500 errors, rate limits - **Concurrent operations**: Race conditions, optimistic UI updates ### QA Requirements - Test all **error states** (not just success paths) - Verify **data persistence** (especially for optimistic UI) - Check **boundary values** (0, null, max length) - Validate **media handling** (images, audio, non-text) ### Test Framework Setup Add to `package.json`: ```json "scripts": { "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage" } ``` Install: ```bash npm install -D vitest @vitest/coverage-v8 ``` Create `vitest.config.ts`: ```typescript import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { environment: 'node', include: ['src/**/__tests__/**/*.test.ts'], }, }); ``` --- ## Dependencies ### Package Management - Use `npm` (not yarn or pnpm) for consistency - Lock versions in `package.json` for production stability - Audit dependencies regularly: `npm audit` ### Common Stack - **Frontend**: React 19+, Next.js 16+, Tailwind CSS 4+ - **Database**: Supabase (PostgreSQL), use `@supabase/supabase-js` - **AI**: Google Gemini (`@google/genai`), OpenAI, or Anthropic - **Analytics**: PostHog (`posthog-js` + `posthog-node`) - **Error Tracking**: Sentry (`@sentry/nextjs`) — mandatory in all apps - **UI Libraries**: Framer Motion, Lucide Icons, Radix UI ### Shared Utility Templates Before writing `posthog.ts`, `db.ts`, or error handling from scratch, copy from `/libs/shared/`: | Template | Copy to | Purpose | |---|---|---| | `libs/shared/posthog.ts` | `src/lib/posthog.ts` | PostHog client + server setup, captureServerEvent() | | `libs/shared/db.ts` | `src/lib/db.ts` | Supabase client/admin, fetchList(), fetchByIds() | | `libs/shared/error-handler.ts` | `src/lib/error-handler.ts` | API errors, AI parsing, timeout wrapper, auth validation | These templates encode all lessons from past postmortems. Do not reinvent them. --- ## Comments & Documentation ### When to Comment - **Complex logic**: Explain non-obvious algorithms or business rules - **Workarounds**: Document why something is done a specific way - **TODOs**: Mark incomplete or temporary code with `// TODO: [reason]` ### When NOT to Comment - **Obvious code**: Don't comment what the code already says - **Over-documentation**: Code should be self-explanatory through naming ```typescript // GOOD - Explains WHY // Strip markdown codeblocks because Gemini sometimes wraps JSON in ```json blocks const cleanText = resultText.replace(/```json\n?/g, ''); // BAD - Explains WHAT (code already says this) // Parse the JSON const result = JSON.parse(cleanText); ``` --- ## Error Tracking (Sentry) Sentry is a mandatory dependency for all apps. It is not optional for MVP. **Setup**: ```bash npm install @sentry/nextjs npx @sentry/wizard@latest -i nextjs ``` **Required files** (wizard creates these, verify they exist): ``` apps/[project]/ sentry.client.config.ts # Browser error capture sentry.server.config.ts # Server/edge error capture next.config.ts # Must use withSentryConfig() ``` **Minimum configuration**: ```typescript // sentry.client.config.ts import * as Sentry from "@sentry/nextjs"; Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, tracesSampleRate: 1.0, environment: process.env.NODE_ENV, }); ``` **Required in every try/catch block in API routes**: ```typescript try { // ... } catch (e) { Sentry.captureException(e); console.error("[route-name] failed:", e); return NextResponse.json({ error: "Internal server error" }, { status: 500 }); } ``` **Env vars** (add to `.env.local.example`): ``` NEXT_PUBLIC_SENTRY_DSN=your-dsn-here SENTRY_AUTH_TOKEN=your-auth-token-here ``` **Rule**: An app deployed without Sentry cannot be debugged in production. Deploy-check will block if Sentry is not configured. // Added: 2026-03-22 — Sentry integration (error tracking gap) --- ## Git Workflow ### Commit Messages - Use conventional commits: `feat:`, `fix:`, `docs:`, `chore:` - Keep first line under 72 characters - Add body for complex changes explaining WHY ``` feat(clarity): add PUT endpoint for task status persistence Implements backend persistence for optimistic UI updates to prevent tasks from reappearing after page reload. Fixes peer-review finding E1. ``` ### Branch Strategy - `main`: Production-ready code - Feature branches: `feature/[issue-number]-[short-description]` - Merge only after passing all quality gates --- ## Lessons Learned (Auto-Generated from Postmortems) The following rules are extracted from actual production issues: 1. **Unbounded pagination loops MUST have page limits AND date bounds** 2. **AI summarization MUST use full payloads, not snippets** 3. **Cron jobs MUST use fan-out architecture for per-user processing** 4. **Third-party error handling MUST distinguish transient vs permanent failures** 5. **Database schemas MUST be verified in deploy-check before build validation** 6. **Serverless API routes MUST await all async calls before returning response** 7. **Cron jobs MUST use batch fetching and concurrent Promise resolution** 8. **Every GET/list query MUST enforce a hard limit() clause** 9. **No optimistic UI mutation without a backend persistence endpoint** 10. **Telemetry MUST be implemented during feature development, not post-QA** 11. **Unauthenticated endpoints calling paid APIs MUST specify rate limiting in the architecture spec** 12. **SessionIds used across analytics + API + DB MUST be generated before any downstream operations** 13. **AI calls on Vercel MUST use AbortController ≤ 9s and return JSON 504 on timeout** 14. **Clipboard copy MUST have navigator.clipboard → execCommand fallback + inline error state** 15. **All API route branches (success, timeout, error, rate-limit) MUST have PostHog events** --- ## Enforcement These standards are enforced through: - **Code Review Agent**: Checks for common violations - **Peer Review Agent**: Adversarial architecture review - **QA Agent**: Validates edge cases and error handling - **Deploy Check Agent**: Verifies production readiness All agents are expected to reference this file before generating or reviewing code.