GameCraftGameCraft

Product Ready Standards

Production-ready quality standards and best practices for ProductReady - build it right the first time

Product Ready Standards

ProductReady is built to product-ready standards. This guide explains the quality practices baked into the boilerplate and how to maintain them as you build.

Why This Matters: Following these standards means fewer bugs, easier maintenance, better performance, and happier users. Ship quality, ship fast.


📋 Product Definition Standards

"Product Ready" starts before you write code. A well-defined product prevents wasted engineering effort.

1. Product Requirements (PRD)

Don't just "start coding". Define what you are building and why.

  • Problem Statement: What pain point are you solving?
  • User Stories: "As a [user], I want to [action] so that [benefit]"
  • Success Metrics: How will you know it's working?

2. Ideal Customer Profile (ICP)

Know who you are building for.

  • Demographics: Role, industry, company size
  • Psychographics: Goals, fears, motivations
  • Buying Trigger: What makes them look for a solution?

3. Marketing Strategy

Build with distribution in mind.

  • Value Proposition: One sentence that explains your product
  • Channels: Where do your users hang out?
  • Launch Plan: How will you get your first 10 users?

✅ Product Ready Checklist

Use this comprehensive checklist to verify your application is truly "Product Ready" before launch. This mirrors the quality criteria from the monorepo's App Status table.

Legend: ✅ Required for 100% score | ⭐ Highly Recommended | 📝 Optional but adds value

1. Product & Design (The "Soul")

RequirementVerification MethodDescription
Product SpecsFile Check: apps/XXX/spec/*.mdRequired: product-design, icp-guide, marketing-guide, onboarding-story, design-system, vi
Visual IdentityVisual Check: Brand ConsistencyBrand colors, typography, and voice defined and consistent
Design SystemVisual Check: UI ConsistencyTokens for colors, spacing, radius defined and used
Theme SyncVisual Check: Theme ToggleCSS variables match vi.md and design-system.md
Brand AssetsVisual Check: Logo DisplayLogo files (SVG/PNG) display correctly in light/dark modes

2. User Experience (The "Body")

RequirementVerification MethodDescription
Landing PageURL Check: /High-converting landing page with clear value prop
Auth ExperienceInteraction: Sign In/Up FlowPolished OAuth buttons, magic links, and error states
Demo ModeURL Check: ?demo=1Instant access for users without login/database
DashboardURL Check: /dashboardProtected app layout with sidebar/navigation
Mobile ResponsiveVisual Check: Resize WindowUI works perfectly on mobile devices (375px width)
Dark ModeVisual Check: Toggle ThemeFirst-class dark mode support without glitches

3. Assets & Metadata (The "Face")

RequirementVerification MethodDescription
FaviconURL Check/favicon.ico loads correctly
App IconURL Check/icon.svg (or png) loads correctly
Open GraphURL Check/opengraph-image generates correct social preview
ScreenshotsFile Checkpublic/screenshots/ contains HD marketing images
ManifestURL Check/manifest.webmanifest returns valid JSON
RequirementVerification MethodDescription
About PageURL Check/about App Store-style product showcase page
Brand GuidelinesURL Check/brand brand guidelines with logo, colors, typography
Privacy PolicyURL Check/privacy page exists and is accessible
Terms of ServiceURL Check/terms page exists and is accessible
Contact/SupportVisual CheckFooter or help center provides contact method

5. Engineering & Ops (The "Brain")

RequirementVerification MethodDescription
Type SafetyBuild Checkpnpm typecheck passes with no errors
SEO SitemapURL Check/sitemap.xml returns valid XML
SEO RobotsURL Check/robots.txt exists and correctly allows/disallows
AI CrawlerURL Check/llms.txt crawler instructions for LLMs
Health CheckURL Check/api/health returns JSON status

6. Content & i18n (The "Voice")

RequirementVerification MethodDescription
DocumentationURL Check/docs or /help renders content correctly
LocalizationURL Check/en, /zh etc prefixes work (if multi-lang)
TranslationsVisual CheckUI text changes correctly when switching language

7. Capabilities & Integrations (The "Muscle")

CapabilityVerification MethodDescription
PaymentsInteraction CheckCan initiate checkout flow (even in test mode)
Open APIURL Check/api/openapi or /api/docs accessible
MCP GatewayURL Check/sse/mcp endpoint connects successfully
Admin PanelURL Check/admin accessible to authorized users
Email DeliveryInteraction CheckTransactional emails (welcome, magic link) arrive

8. Quality Assurance (The "Shield")

RequirementVerification MethodDescription
E2E TestsCommand Checkpnpm test:e2e passes critical flows
Unit TestsCommand Checkpnpm test passes business logic tests
LintingCommand Checkpnpm lint passes with no warnings

🎨 Design Product-Ready Standards

Design System Architecture

ProductReady follows a strict design hierarchy to ensure consistency:

  1. Brand Identity: Your colors, typography, and voice (The "Soul")
  2. Design Tokens: Semantic names for values (e.g., primary-color, spacing-md)
  3. CSS Variables: The technical implementation in global.css
  4. Components: UI elements consuming variables

🚨 Component Library Integrity (KUI)

DO NOT modify packages/kui/* directly.

packages/kui is the shared foundation library. Modifying it risks breaking multiple applications.

Correct way to customize:

  1. Import the component: import { Button } from 'kui/button';
  2. Create a wrapper in your app: apps/XXX/src/components/ui/my-button.tsx
  3. Apply custom styles via CSS variables or className props.

CSS Variables (No Hardcoding!)

Always use CSS variables for colors, spacing, etc:

// ✅ Good: Uses CSS variables
<div className="bg-primary text-primary-foreground">
<button className="bg-accent hover:bg-accent/90">

// ❌ Bad: Hardcoded colors
<div className="bg-blue-500 text-white">
<button className="bg-green-600 hover:bg-green-700">

Dark Mode Support

All UI must work in dark mode:

/* global.css */
:root {
  --background: 0 0% 100%;  /* White */
  --foreground: 222 47% 11%;  /* Dark text */
}

.dark {
  --background: 224 71% 4%;  /* Dark bg */
  --foreground: 213 31% 91%;  /* Light text */
}

Test dark mode: Toggle in browser or use ?theme=dark

Responsive Design

Mobile-first approach:

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
  {/* 1 column mobile, 2 tablet, 3 desktop */}
</div>

Test on:

  • iPhone (375px width)
  • iPad (768px)
  • Desktop (1280px+)

📊 Accessibility Standards

WCAG 2.1 AA Compliance

Minimum requirements:

Color Contrast:

Keyboard Navigation:

// All interactive elements must be keyboard accessible
<button onClick={handleClick}>Click Me</button>  ✅
<div onClick={handleClick}>Click Me</div>        ❌

Focus Indicators:

button:focus-visible {
  outline: 2px solid hsl(var(--primary));
  outline-offset: 2px;
}

Alt Text for Images:

<img src="/logo.png" alt="ProductReady Logo" />

Semantic HTML:

<nav>, <main>, <article>, <button>
❌ <div className="nav">, <div onClick={...}>

🔗 Demo Mode Standard

ProductReady includes demo mode (?demo=1) for testing without database:

// src/lib/demo.ts
export function isDemoMode(searchParams?: URLSearchParams): boolean {
  return searchParams?.get('demo') === '1';
}

// In tRPC router
export const tasksRouter = createTRPCRouter({
  list: publicProcedure
    .input(z.object({ demo: z.boolean().optional() }))
    .query(async ({ ctx, input }) => {
      if (input.demo) {
        return { tasks: getDemoTasks() };  // Mock data
      }
      
      return { tasks: await ctx.db.select().from(tasks) };
    }),
});

Benefits:

  • ✅ Instant testing (no database setup)
  • ✅ Demos and presentations
  • ✅ UI development without backend

🎯 Lighthouse Scores (Target)

Run Lighthouse audit in Chrome DevTools:

Minimum scores:

  • Performance: 90+
  • Accessibility: 95+
  • Best Practices: 95+
  • 🔍 SEO: 95+

Common issues and fixes:

IssueFix
Large imagesUse Next.js <Image> with optimization
Unused JavaScriptCode split with dynamic imports
Missing alt textAdd to all <img> tags
Missing meta tagsAdd in layout.tsx metadata

📝 Documentation Standards

Code Comments

Use TSDoc for functions and components:

/**
 * Creates a new task with AI-generated description
 * @param input - Task title and priority
 * @returns Created task with generated description
 * @throws {Error} If AI service is unavailable
 */
export async function createTaskWithAI(
  input: CreateTaskInput
): Promise<Task> {
  const description = await generateDescription(input.title);
  return db.insert(tasks).values({ ...input, description });
}

README Updates

Keep your README current:

  • ✅ Update when adding major features
  • ✅ Keep setup instructions accurate
  • ✅ Document new environment variables
  • ✅ Add links to new documentation

🔄 Maintenance Standards

Dependency Updates

Monthly:

# Check for updates
pnpm outdated

# Update interactive
pnpm update -i

# Test after updating
pnpm typecheck
pnpm lint
pnpm test
pnpm build

Security Audits

Weekly:

# Check for vulnerabilities
pnpm audit

# Fix automatically
pnpm audit --fix

Changelog Discipline

Create changelog for:

  • ✅ New features
  • ✅ Breaking changes
  • ✅ Bug fixes (significant)
  • ✅ Performance improvements
  • ✅ Security updates

Format: apps/productready/changelog/YYYYMMDD-description.md


🎯 Core Quality Principles

1. Type Safety Everywhere

TypeScript strict mode catches bugs before they reach production.

// ✅ Good: Full type safety
interface CreateTaskInput {
  title: string;
  description?: string;
  priority: 'low' | 'medium' | 'high' | 'urgent';
}

function createTask(input: CreateTaskInput): Promise<Task> {
  // TypeScript knows exactly what input contains
  return db.insert(tasks).values(input).returning();
}

// ❌ Bad: Using `any` loses all safety
function createTask(input: any) {
  // No idea what input contains - errors at runtime!
  return db.insert(tasks).values(input);
}

Benefits:

  • ✅ Auto-completion in your editor
  • ✅ Catch typos and missing properties
  • ✅ Refactoring is safe and easy
  • ✅ Self-documenting code

2. Runtime Validation with Zod

TypeScript only checks at compile-time. Zod validates at runtime (when data comes from users, APIs, or databases).

import { z } from 'zod';

// Define schema
const CreateTaskSchema = z.object({
  title: z.string().min(1).max(255),
  description: z.string().optional(),
  priority: z.enum(['low', 'medium', 'high', 'urgent']).default('medium'),
});

// Use in tRPC
export const tasksRouter = createTRPCRouter({
  create: protectedProcedure
    .input(CreateTaskSchema)  // ✅ Validates input
    .mutation(async ({ input }) => {
      // input is guaranteed valid here
      return db.insert(tasks).values(input);
    }),
});

What Zod Catches:

  • Invalid data types (string vs number)
  • Missing required fields
  • Values outside allowed range
  • Malformed emails, URLs, etc.

3. VO/DTO/PO Pattern

Separate concerns for data at different layers:

LayerTypePurposeExample
Database (PO)Persistent ObjectRaw database schemaIncludes password hash, internal IDs
API (DTO)Data Transfer ObjectInput/output validationUser provides email, password
UI (VO)View ObjectFrontend displayShow user name, email, avatar
// Database layer (PO) - Never expose directly!
const userPO = await db.query.users.findFirst({ 
  where: eq(users.id, userId) 
});
// Contains: passwordHash, stripeCustomerId, etc.

// API layer (DTO) - Validated input
const CreateUserDTO = z.object({
  email: z.string().email(),
  password: z.string().min(8),
  name: z.string(),
});

// UI layer (VO) - Safe to return to frontend
interface UserVO {
  id: string;
  email: string;
  name: string;
  avatar: string | null;
  createdAt: string;  // ISO string, not Date object
}

// Transform PO → VO before returning
function toUserVO(po: UserPO): UserVO {
  return {
    id: po.id,
    email: po.email,
    name: po.name,
    avatar: po.image,
    createdAt: po.createdAt.toISOString(),
  };
}

Security: Never return database objects directly to the frontend. Always transform through a VO.


🔒 Security Standards

Authentication & Authorization

ProductReady uses Better Auth with secure defaults:

What's Already Configured:

  • Password hashing (bcrypt)
  • Session management (httpOnly cookies)
  • CSRF protection
  • Secure cookie flags (production only)

Your Responsibility:

// ✅ Always use protectedProcedure for sensitive operations
export const tasksRouter = createTRPCRouter({
  delete: protectedProcedure  // Requires authentication
    .input(z.object({ id: z.number() }))
    .mutation(async ({ ctx, input }) => {
      // ctx.session.user is guaranteed to exist
      const userId = ctx.session.user.id;
      
      // ✅ Check ownership before deleting
      const task = await db.query.tasks.findFirst({
        where: eq(tasks.id, input.id),
      });
      
      if (!task || task.userId !== userId) {
        throw new Error('Not authorized');
      }
      
      await db.delete(tasks).where(eq(tasks.id, input.id));
    }),
});

Environment Variables

Never commit secrets!

# ✅ Good: Use .env files (git ignored)
PG_DATABASE_URL="postgresql://..."
BETTER_AUTH_SECRET="..."

# ❌ Bad: Hardcoded in code
const secret = "super-secret-key-123";  // NEVER DO THIS!

Prefix for public vars:

# ✅ Available in browser
NEXT_PUBLIC_APP_NAME="ProductReady"

# ✅ Server-only (secure)
PG_DATABASE_URL="postgresql://..."
STRIPE_SECRET_KEY="sk_live_..."

📊 Performance Standards

Database Queries

Avoid N+1 queries - Use joins instead of loops:

// ❌ Bad: N+1 query (1 + N queries)
const tasks = await db.select().from(tasks);
for (const task of tasks) {
  const user = await db.select().from(users)
    .where(eq(users.id, task.userId));  // N queries!
}

// ✅ Good: Single query with join
const tasksWithUsers = await db
  .select({
    task: tasks,
    user: users,
  })
  .from(tasks)
  .leftJoin(users, eq(tasks.userId, users.id));

Pagination

Always paginate large lists:

export const tasksRouter = createTRPCRouter({
  list: protectedProcedure
    .input(z.object({
      page: z.number().min(1).default(1),
      limit: z.number().min(1).max(100).default(20),
    }))
    .query(async ({ ctx, input }) => {
      const offset = (input.page - 1) * input.limit;
      
      const tasks = await ctx.db
        .select()
        .from(tasks)
        .limit(input.limit)
        .offset(offset);
      
      return { tasks, page: input.page, limit: input.limit };
    }),
});

Connection Pooling

Critical for Vercel/serverless!

Use pooled database connections:

# Neon - use port 6543 (pooled) instead of 5432
PG_DATABASE_URL="postgresql://user:pass@host:6543/db"

# Supabase - use "connection pooling" string
PG_DATABASE_URL="postgresql://...pooler.supabase.com:6543/..."

🧪 Testing Standards

E2E Tests (Playwright)

ProductReady includes smoke tests. Add more for critical flows:

// tests/e2e/auth.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Authentication', () => {
  test('user can sign up', async ({ page }) => {
    await page.goto('/signup');
    
    await page.fill('input[name="email"]', 'test@example.com');
    await page.fill('input[name="password"]', 'password123');
    await page.fill('input[name="name"]', 'Test User');
    
    await page.click('button[type="submit"]');
    
    // Should redirect to dashboard
    await expect(page).toHaveURL('/dashboard');
  });
});

Run tests:

pnpm test:e2e       # Headless
pnpm test:e2e:ui    # Interactive mode

Unit Tests (Vitest)

Test business logic and utilities:

// src/lib/utils.test.ts
import { describe, it, expect } from 'vitest';
import { formatCurrency } from './utils';

describe('formatCurrency', () => {
  it('formats USD correctly', () => {
    expect(formatCurrency(1000, 'USD')).toBe('$1,000.00');
  });
  
  it('handles zero', () => {
    expect(formatCurrency(0, 'USD')).toBe('$0.00');
  });
});

📐 Code Product-Ready Standards

Linting & Formatting

ProductReady uses Biome (faster than ESLint + Prettier):

# Format and lint
pnpm lint

# Auto-fix issues
pnpm lint --write

Pre-commit hooks: Configured with Lefthook (runs on every commit)

TypeScript Strictness

All apps must have strict mode enabled:

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}

Component Patterns

Keep components small and focused:

// ✅ Good: Single responsibility
function TaskCard({ task }: { task: Task }) {
  return (
    <div className="card">
      <h3>{task.title}</h3>
      <p>{task.description}</p>
    </div>
  );
}

function TaskList({ tasks }: { tasks: Task[] }) {
  return (
    <div className="grid gap-4">
      {tasks.map(task => <TaskCard key={task.id} task={task} />)}
    </div>
  );
}

// ❌ Bad: Too many responsibilities
function TaskPage() {
  // Fetching, filtering, sorting, rendering all in one component
  // 500 lines of code...
}

🚀 Production Standards

Health Check Endpoint

ProductReady includes /api/health for monitoring:

// src/app/api/health/route.ts
export async function GET() {
  return Response.json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    service: 'productready',
    version: process.env.VERSION || 'unknown',
    buildNumber: process.env.NEXT_PUBLIC_BUILD_NUMBER || '0',
  });
}

Use for:

  • Uptime monitoring (UptimeRobot, Better Uptime)
  • Load balancer health checks
  • Deployment verification

Error Tracking

Recommended: Sentry or similar

// src/app/layout.tsx
import * as Sentry from '@sentry/nextjs';

if (process.env.NODE_ENV === 'production') {
  Sentry.init({
    dsn: process.env.SENTRY_DSN,
    tracesSampleRate: 0.1,
  });
}

📝 Documentation Standards

Code Comments

Use TSDoc for functions and components:

/**
 * Creates a new task with AI-generated description
 * @param input - Task title and priority
 * @returns Created task with generated description
 * @throws {Error} If AI service is unavailable
 */
export async function createTaskWithAI(
  input: CreateTaskInput
): Promise<Task> {
  const description = await generateDescription(input.title);
  return db.insert(tasks).values({ ...input, description });
}

README Updates

Keep your README current:

  • ✅ Update when adding major features
  • ✅ Keep setup instructions accurate
  • ✅ Document new environment variables
  • ✅ Add links to new documentation

🔄 Maintenance Standards

Dependency Updates

Monthly:

# Check for updates
pnpm outdated

# Update interactive
pnpm update -i

# Test after updating
pnpm typecheck
pnpm lint
pnpm test
pnpm build

Security Audits

Weekly:

# Check for vulnerabilities
pnpm audit

# Fix automatically
pnpm audit --fix

Changelog Discipline

Create changelog for:

  • ✅ New features
  • ✅ Breaking changes
  • ✅ Bug fixes (significant)
  • ✅ Performance improvements
  • ✅ Security updates

Format: apps/productready/changelog/YYYYMMDD-description.md


🎯 Lighthouse Scores (Target)

Run Lighthouse audit in Chrome DevTools:

Minimum scores:

  • Performance: 90+
  • Accessibility: 95+
  • Best Practices: 95+
  • 🔍 SEO: 95+

Common issues and fixes:

IssueFix
Large imagesUse Next.js <Image> with optimization
Unused JavaScriptCode split with dynamic imports
Missing alt textAdd to all <img> tags
Missing meta tagsAdd in layout.tsx metadata

🔗 Demo Mode Standard

ProductReady includes demo mode (?demo=1) for testing without database:

// src/lib/demo.ts
export function isDemoMode(searchParams?: URLSearchParams): boolean {
  return searchParams?.get('demo') === '1';
}

// In tRPC router
export const tasksRouter = createTRPCRouter({
  list: publicProcedure
    .input(z.object({ demo: z.boolean().optional() }))
    .query(async ({ ctx, input }) => {
      if (input.demo) {
        return { tasks: getDemoTasks() };  // Mock data
      }
      
      return { tasks: await ctx.db.select().from(tasks) };
    }),
});

Benefits:

  • ✅ Instant testing (no database setup)
  • ✅ Demos and presentations
  • ✅ UI development without backend

📚 Further Reading


🆘 Quality Issues?

If you find quality issues in ProductReady:

  1. Report: Create GitHub Issue
  2. Fix: Submit PR with fix + tests
  3. Document: Update this guide if needed

Remember: Quality is not a one-time thing. It's a continuous practice. Build it into your workflow! 🚀

On this page

Product Ready Standards📋 Product Definition Standards1. Product Requirements (PRD)2. Ideal Customer Profile (ICP)3. Marketing Strategy✅ Product Ready Checklist1. Product & Design (The "Soul")2. User Experience (The "Body")3. Assets & Metadata (The "Face")4. Trust & Legal (The "Suit")5. Engineering & Ops (The "Brain")6. Content & i18n (The "Voice")7. Capabilities & Integrations (The "Muscle")8. Quality Assurance (The "Shield")🎨 Design Product-Ready StandardsDesign System Architecture🚨 Component Library Integrity (KUI)CSS Variables (No Hardcoding!)Dark Mode SupportResponsive Design📊 Accessibility StandardsWCAG 2.1 AA Compliance🔗 Demo Mode Standard🎯 Lighthouse Scores (Target)📝 Documentation StandardsCode CommentsREADME Updates🔄 Maintenance StandardsDependency UpdatesSecurity AuditsChangelog Discipline🎯 Core Quality Principles1. Type Safety Everywhere2. Runtime Validation with Zod3. VO/DTO/PO Pattern🔒 Security StandardsAuthentication & AuthorizationEnvironment Variables📊 Performance StandardsDatabase QueriesPaginationConnection Pooling🧪 Testing StandardsE2E Tests (Playwright)Unit Tests (Vitest)📐 Code Product-Ready StandardsLinting & FormattingTypeScript StrictnessComponent Patterns🚀 Production StandardsHealth Check EndpointError Tracking📝 Documentation StandardsCode CommentsREADME Updates🔄 Maintenance StandardsDependency UpdatesSecurity AuditsChangelog Discipline🎯 Lighthouse Scores (Target)🔗 Demo Mode Standard📚 Further Reading🆘 Quality Issues?