t interface UserEntity {
id: string;
email: string;
role: 'admin' | 'editor' | 'viewer';
createdAt: Date;
}
// src/modules/user/infrastructure/user.repository.ts
import { UserEntity } from '../domain/user.entity';
export interface UserRepository {
findById(id: string): Promise<UserEntity | null>;
create(payload: Omit<UserEntity, 'id' | 'createdAt'>): Promise<UserEntity>;
}
**Rationale:** Defining interfaces and entities before prompting the AI forces the model to generate implementations that conform to explicit contracts. This prevents the AI from embedding business logic directly into route handlers or UI components.
### Step 2: Implement AI Output Validation Gates
Never merge AI-generated code without automated structural validation. Create a lightweight validation layer that checks for common AI failure patterns: missing error boundaries, untyped returns, and direct database coupling.
```typescript
// src/guards/ai-output-validator.ts
import { ESLint } from 'eslint';
import { execSync } from 'child_process';
export async function validateAiOutput(filePath: string): Promise<boolean> {
const eslint = new ESLint({ overrideConfigFile: './eslint.ai.config.js' });
const results = await eslint.lintFiles([filePath]);
const hasCriticalViolations = results.some(
result => result.messages.some(msg => msg.severity === 2)
);
if (hasCriticalViolations) {
console.warn(`[AI-Guard] Critical violations detected in ${filePath}`);
return false;
}
// Run deterministic type-checking
try {
execSync(`npx tsc --noEmit ${filePath}`, { stdio: 'inherit' });
return true;
} catch {
return false;
}
}
Rationale: AI models frequently omit explicit return types or bypass error handling for brevity. This gate enforces strict TypeScript compliance and runs a targeted linting pass before the code enters the main branch. It transforms AI output from a black box into a verifiable artifact.
Step 3: Decouple Infrastructure from Business Logic
Platform-specific abstractions (e.g., native Supabase clients, Replit deployment hooks) create extraction friction. Abstract infrastructure behind standard interfaces.
// src/modules/database/infrastructure/postgres.adapter.ts
import { Pool, PoolClient } from 'pg';
import { DatabaseAdapter } from './database.adapter.interface';
export class PostgresAdapter implements DatabaseAdapter {
private pool: Pool;
constructor(connectionString: string) {
this.pool = new Pool({ connectionString });
}
async transaction<T>(callback: (client: PoolClient) => Promise<T>): Promise<T> {
const client = await this.pool.connect();
try {
await client.query('BEGIN');
const result = await callback(client);
await client.query('COMMIT');
return result;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
}
Rationale: By wrapping platform-specific drivers behind a standard DatabaseAdapter interface, you preserve the ability to migrate cloud providers or switch database engines without rewriting business logic. This directly counters the lock-in risk highlighted in rapid-prototyping tools.
Step 4: Integrate Deterministic CI/CD Gates
AI-assisted development requires stricter pipeline controls than traditional workflows. Enforce automated testing, dependency auditing, and architecture compliance checks.
# .github/workflows/ai-assisted-pipeline.yml
name: AI-Assisted Validation Pipeline
on:
pull_request:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run lint:strict
- run: npm run test:coverage
- run: npm run audit:deps
- run: npm run build:prod
Rationale: Traditional pipelines assume human-authored code follows consistent patterns. AI-generated code does not. Adding strict linting, coverage thresholds, and dependency auditing catches structural drift before it compounds.
Pitfall Guide
1. The Monolith Prompt Trap
Explanation: Prompting an AI to "build the entire auth system" generates a single file containing routing, validation, database queries, and session management. This violates separation of concerns and makes unit testing impossible.
Fix: Decompose prompts into domain-specific tasks. Prompt for the repository layer first, then the service layer, then the controller. Enforce file boundaries using project templates.
2. Implicit Dependency Injection
Explanation: AI models frequently instantiate dependencies directly inside functions (const db = new SupabaseClient()) rather than accepting them as parameters. This creates tight coupling and prevents mocking during tests.
Fix: Mandate constructor or parameter injection in all service classes. Use TypeScript interfaces to define dependencies explicitly. Add a lint rule that flags direct new instantiations of infrastructure classes outside factory modules.
3. Skipping Schema Migration Versioning
Explanation: AI-generated database changes often modify tables directly without version control. When multiple developers or AI sessions alter the schema, state drift occurs and rollbacks become impossible.
Fix: Integrate a migration runner (e.g., node-pg-migrate, Drizzle Kit, or Prisma Migrate). Require all schema changes to be submitted as versioned migration files. Never allow direct DDL execution in production environments.
4. Trusting AI-Generated Authentication Flows
Explanation: AI models frequently omit critical security controls: missing CSRF tokens, improper session expiration, or weak password hashing algorithms. These vulnerabilities remain hidden until penetration testing or exploitation.
Fix: Treat all AI-generated auth code as untrusted. Enforce OWASP ASVS compliance checks. Use established libraries (bcrypt, argon2, jose) and mandate code review for any session management logic. Run automated SAST scans on every merge.
5. Ignoring CI/CD Pipeline Integration
Explanation: Developers using cloud sandboxes or visual prototypers often bypass version control and automated testing. Code lives in platform-specific environments, making collaboration, rollback, and audit trails impossible.
Fix: Treat the local repository as the source of truth. Sync AI-generated code to Git immediately. Enforce branch protection rules requiring passing CI checks before merging. Never deploy directly from a sandbox environment.
Explanation: Tools like Lovable or Replit generate code tightly coupled to their native ecosystems (e.g., Supabase RPC functions, Replit deployment hooks). Extracting this code for custom cloud architecture requires manual refactoring and introduces regression risk.
Fix: Abstract platform APIs behind standard interfaces. Use environment variables for configuration. Maintain a platform-adapter layer that isolates vendor-specific logic. Test extraction workflows quarterly.
7. Over-Reliance on AI for Error Handling
Explanation: AI models prioritize happy-path logic and frequently omit comprehensive error boundaries, fallback mechanisms, or structured logging. Production systems fail silently or expose stack traces.
Fix: Implement a centralized error handling middleware. Require explicit try/catch blocks with typed error classes. Enforce structured logging (pino, winston) with correlation IDs. Add integration tests that simulate failure states.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Early-stage MVP validation | Lovable or Replit sandbox | Maximizes speed for investor demos and user feedback loops | Low initial cost, high extraction cost if scaling |
| Internal team scaling | Cursor with local IDE fork | Preserves Git workflows, CI/CD integration, and developer control | Moderate setup cost, low long-term maintenance |
| Enterprise compliance required | Custom TypeScript stack + Cursor assist | Enables strict auditing, migration versioning, and security gates | High initial engineering cost, minimal technical debt |
| Multi-cloud deployment | Abstracted adapter layer + Cursor | Decouples business logic from infrastructure providers | Moderate abstraction cost, high portability ROI |
Configuration Template
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
// eslint.ai.config.js
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/strict'
],
rules: {
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/no-unsafe-assignment': 'error',
'@typescript-eslint/no-unsafe-member-access': 'error',
'no-console': ['warn', { allow: ['warn', 'error'] }],
'prefer-const': 'error'
}
};
# docker-compose.dev.yml
version: '3.9'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://dev:dev@db:5432/app_dev
depends_on:
- db
- redis
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
POSTGRES_DB: app_dev
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
pgdata:
Quick Start Guide
- Initialize the repository: Run
npm init -y && npm install typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint vitest. Create tsconfig.json and eslint.ai.config.js from the template above.
- Generate domain scaffolding: Use your AI assistant to create interface definitions for core entities and repositories. Do not generate implementations yet. Verify strict typing with
npx tsc --noEmit.
- Implement validation gates: Add the
ai-output-validator.ts script to your pre-commit hook (husky or lint-staged). Configure it to run ESLint and TypeScript checks on staged files.
- Wire CI/CD enforcement: Copy the GitHub Actions workflow to
.github/workflows/. Require status checks on pull requests. Run npm run test:coverage and npm run audit:deps on every merge.
- Abstract infrastructure: Replace direct platform clients with adapter interfaces. Implement
PostgresAdapter or equivalent. Verify migration versioning works with your chosen runner. Commit and push.