In the ground since Sat Nov 08 2025
Last watered inSun Nov 16 2025
Recap
Successfully completed Phase 0 (Prerequisites + Package Setup) for the Peek-a-boo React SDK. This phase accomplished two major goals:
-
Prerequisites: Implemented environment-aware feature flags with proper database schema, migrations, and SDK service endpoints. The critical blocker was lack of environment support in the database schema - feature flags were only scoped to organization/project without dev/staging/prod separation.
-
Package Setup: Created the @peek-a-boo/react-sdk package structure with TypeScript, Vite build configuration, and monorepo integration ready for Phase 1 implementation.
Key Achievement: Created a production-ready foundation where flags can be managed per environment, with proper API endpoints for the React SDK to consume, and a zero-dependency package structure ready for core client implementation.
The Problem
Initial assessment revealed the React SDK design assumed:
But the database schema had NO environment concept:
Flags were only scoped to Organization + Project. This wouldn't support:
- Different flag states in dev vs production
- Safe testing before production rollout
- Environment-specific feature releases
The Solution
1. Schema Evolution
Added environment support with proper constraints:
Critical Design Decision: The unique constraint [key, projectId, environment] allows the same flag key (e.g., "new-checkout") to exist with different states across environments.
2. Migration Strategy
Lesson Learned: NEVER use workarounds for migrations. Always create proper migration files.
The correct flow:
This creates:
- Migration SQL file in prisma/migrations/
- Updates Prisma client with new types
- Applies changes to database
- Maintains migration history for production deployments
Why this matters:
- ✅ Reproducible across environments
- ✅ Can be committed to git
- ✅ Can be rolled back
- ✅ Safe for production with prisma migrate deploy
3. SDK Service Runtime API
Created a new domain module sdk-runtime (separate from admin feature-flags module):
Design Pattern: Runtime API is separate from admin API because:
- Different authentication (SDK keys vs user auth)
- Different data needs (runtime needs minimal, fast responses)
- Different caching strategies
- Different rate limiting requirements
4. Environment Variable Architecture
Problem: Hardcoded organization IDs scattered across Dashboard and seed file led to foreign key violations after database resets.
Solution: Single source of truth via environment variables:
Seed file uses upsert to ensure consistent org ID:
Benefits:
- Seed always creates same org ID
- Dashboard always uses same org ID
- Easy to change in one place
- No foreign key violations
Anatomy of a Complete System Fix
Phase 1: Schema Changes
- Update Prisma schema
- Run migration (interactive required)
- Verify Prisma client regenerated
Phase 2: Cascade Updates
Think through the entire system:
- Seed file - Must use new required fields
- Service DTOs - Must include new fields
- Dashboard actions - Must send new fields
- API endpoints - Must handle new fields
Critical Thinking Required: When changing schema, don't just fix one file. Trace through:
- Where is data created? (Dashboard form → action → API)
- Where is data read? (API endpoints → SDK)
- Where is data seeded? (Seed file)
Each layer needs updating, not just the schema.
Phase 3: Validation
Test the entire flow:
Package Setup & Monorepo Integration
With the database foundation and API endpoints complete, Phase 0 continues with creating the React SDK package structure and integrating it into the Turborepo monorepo.
Package Structure Created
1packages/react-sdk/
2├── src/
3│ ├── client/ # Vanilla JS client (no React)
4│ ├── react/ # React-specific code
5│ │ └── hooks/ # useFeatureFlag, useFeatureFlags
6│ └── test/ # Test utilities and setup
7├── package.json
8├── tsconfig.json
9└── vite.config.ts
Design Decision: Separate client/ from react/ to enable future framework adapters (Vue, Svelte) to reuse the core client logic.
Package Configuration
packages/react-sdk/package.json:
Key Characteristics:
- Zero runtime dependencies - Only React as peer dependency
- ESM + CJS outputs - Supports both module systems
- Testing stack: Vitest + Testing Library + MSW (for API mocking)
- Target bundle size: less than 10KB (goal from PDR)
TypeScript Configuration
packages/react-sdk/tsconfig.json:
Design Choices:
- strict: true - Catch errors at compile time
- declaration: true - TypeScript users get full autocomplete
- moduleResolution: "bundler" - Modern resolution for Vite
- noEmit: true - Vite handles building, TSC only type-checks
Vite Build Configuration
packages/react-sdk/vite.config.ts:
Build Strategy:
- Library mode - Not building an app, building a distributable package
- External React - Don't bundle React, expect consumer to provide it
- Two formats:
- dist/index.esm.js - For modern bundlers (tree-shakeable)
- dist/index.cjs.js - For legacy Node/CommonJS
Test Strategy:
- jsdom environment (simulates browser DOM)
- Global test utilities (describe, it, expect available everywhere)
- MSW setup file for mocking API requests
Critical Fix: Vitest Types
Initial version used import { defineConfig } from 'vite' which caused TypeScript error:
1Object literal may only specify known properties, and 'test' does not exist
Solution: Import from vitest/config instead:
This provides proper typing for the test configuration block.
Monorepo Integration
The React SDK automatically integrates with the existing Turborepo configuration:
Root turbo.json:
Why it works:
- "^build" dependency means build dependencies first
- "dist/**" output pattern matches React SDK output
- No package-specific configuration needed
Build command:
Installation & Verification
Phase 0 Completion Status
Prerequisites: ✅
- Schema supports environments
- Migration applied
- API endpoints created
- Environment variables configured
Package Setup: ✅
- Directory structure created
- package.json configured
- TypeScript configured
- Vite build configured
- Monorepo integration verified
- Dependencies installed
Ready for Phase 1: ✅ All foundational work complete
Used in Projects
- Peek-a-boo Feature Flag Platform - Foundation for React SDK development
- Pattern applicable to any feature flag system requiring environment separation
Key Lessons
1. Schema Design First
Don't build APIs before validating the database schema supports your use case. The React SDK design assumed environment support - checking the schema FIRST saved potential rework.
2. Think Systemically
Changing a Prisma schema isn't done until:
- Migration created ✅
- Seed updated ✅
- DTOs updated ✅
- API endpoints updated ✅
- Dashboard updated ✅
- All tested ✅
Missing ANY step breaks the system.
3. Environment Variables > Hardcoding
A single hardcoded ID becomes technical debt the moment you need to reset the database. Use env vars from day 1.
4. Separation of Concerns
Runtime API (/api/v1/flags) separate from Admin API (/feature-flags) because they serve different purposes, different clients, different security models.
5. Documentation Matters
The .env.example files with comments explain WHY each variable exists and HOW they relate:
Next Steps
With Phase 0 complete, the React SDK can now proceed:
Phase 1: Core Client Implementation
- TypeScript types matching API response
- HTTP client with retry logic
- FeatureFlagClient (vanilla JS)
The foundation is solid:
- ✅ Schema supports environments
- ✅ API endpoints ready
- ✅ Data seeded and testable
- ✅ Environment variables configured
Technical Debt Identified
-
Hardcoded Organization Concept - Dashboard still requires organization context. Future: Multi-tenant support needed.
-
No API Key Authentication - Runtime endpoints currently accept any projectId. Future: Implement SDK key validation.
-
No Rate Limiting - Production deployment needs rate limiting on runtime endpoints.
-
Manual Param Awaiting - Next.js 15 warnings about params.id needing await. Minor but should be addressed.
Commands Reference
Debugging Tips
Foreign Key Violations:
- Check organization/project IDs exist in database
- Verify env vars match between seed and dashboard
- Use npx prisma studio to inspect actual IDs
Migration Issues:
- Always run from packages/core directory
- Ensure .env file exists in packages/core
- Use prisma migrate reset --force to start fresh (dev only!)
Type Errors After Schema Changes:
- Ensure Prisma client regenerated: npx prisma generate
- Restart TypeScript server in IDE
- Check all imports use Environment enum type, not strings