Frontend-Backend Unification Guide
**Phase:** 298 - Frontend-Backend Unification & Redundancy Removal
**Last Updated:** 2026-04-12
---
Overview
This guide explains how to achieve frontend-backend unification, remove redundant code, prevent bugs, and establish regression prevention mechanisms.
Goals
- **API Contract Testing:** Validate frontend-backend API alignment
- **Redundancy Removal:** Eliminate duplicate code across boundaries
- **Bug Prevention:** Discover and fix integration bugs early
- **Type Safety:** Enforce type safety across API boundaries
- **Quality Gates:** Prevent regressions with automated checks
Key Principles
- **Backend is Single Source of Truth:** Validation, business rules, and data transformations live in the backend
- **Frontend is UX-Only:** Frontend validation is for user experience only (format hints, real-time feedback)
- **Type Safety:** No
anytypes in API layer - all types must be explicit - **Contract Testing:** All API endpoints must have contract tests
- **Regression Prevention:** Quality gates prevent breaking changes
---
API Contract Testing
What is Contract Testing?
Contract testing validates that frontend and backend APIs are aligned:
- Request parameters match
- Response structures match
- Type mappings are correct (TypeScript ↔ Python)
- Auth/tenant requirements are consistent
How to Write Contract Tests
Backend Contract Tests (Python)
# backend-saas/api/__tests__/contract/agent_routes_contract.py
from fastapi.testclient import TestClient
from main_api_app import app
client = TestClient(app)
def test_list_agents_contract():
"""Test GET /api/agents contract"""
response = client.get(
"/api/agents",
headers={"Authorization": "Bearer test-token"}
)
assert response.status_code == 200
data = response.json()
# Verify response structure
assert "agents" in data
assert isinstance(data["agents"], list)
# Verify agent structure
if len(data["agents"]) > 0:
agent = data["agents"][0]
assert "id" in agent
assert "name" in agent
assert "maturity" in agent
assert isinstance(agent["capabilities"], list)Frontend Contract Tests (TypeScript)
// src/lib/api/__tests__/contract-tests/agent-api-contracts.test.ts
import { describe, it, expect } from 'vitest';
import { fetchAgents } from '@/lib/api/agents';
describe('Agent API Contracts', () => {
it('should return agents with correct structure', async () => {
const response = await fetchAgents();
// Verify response structure
expect(response).toHaveProperty('agents');
expect(Array.isArray(response.agents)).toBe(true);
// Verify agent structure
if (response.agents.length > 0) {
const agent = response.agents[0];
expect(agent).toHaveProperty('id');
expect(agent).toHaveProperty('name');
expect(agent).toHaveProperty('maturity');
expect(Array.isArray(agent.capabilities)).toBe(true);
}
});
});Type Mapping Rules
| TypeScript | Python | Example |
|---|---|---|
string | str, UUID, datetime, EmailStr | Agent ID |
number | int, float, Decimal | ACU usage |
boolean | bool | Active status |
Array<T> | List[T] | Capabilities list |
Object | Dict, JSON | Settings object |
Date | datetime | Created at |
Running Contract Tests
# Backend contract tests
cd backend-saas && pytest api/__tests__/contract/
# Frontend contract tests
npm test -- src/lib/api/__tests__/contract-tests/---
Redundancy Detection and Removal
What is Redundancy?
Redundancy is duplicate code across frontend and backend:
- Duplicate validation logic (email regex, password rules)
- Duplicate data transformations (date formatting, currency rounding)
- Duplicate business rules (agent limits, quota checks)
How to Detect Redundancy
Run Redundancy Detection Script
cd backend-saas
python3 scripts/detect_redundancy.py \
--frontend ../src \
--backend . \
--output .planning/phases/298-frontend-backend-unificationManual Inspection
# Find duplicate validation logic
grep -r "email.*regex" src/ backend-saas/
# Find duplicate date formatting
grep -r "toISOString\|strftime" src/ backend-saas/
# Find duplicate business rules
grep -r "agent.*limit\|quota.*check" src/ backend-saas/How to Remove Redundancy
1. Keep Backend Only
**Before (Redundant):**
// src/lib/validation/email.ts (frontend)
export function validateEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}# backend-saas/core/validation.py (backend)
def validate_email(email: str) -> bool:
email_regex = re.compile(r'^[^\s@]+@[^\s@]+\.[^\s@]+$')
return bool(email_regex.match(email))**After (Backend Only):**
// src/lib/validation/email.ts (frontend)
// Re-export from backend validation
// Backend is single source of truth
export const validateEmail = (email: string): boolean => {
// Backend validates this - frontend only checks format for UX
return email.includes('@'); // Simple UX check only
};# backend-saas/core/validation.py (backend)
def validate_email(email: str) -> bool:
"""Validate email address (single source of truth)"""
email_regex = re.compile(r'^[^\s@]+@[^\s@]+\.[^\s@]+$')
return bool(email_regex.match(email))2. Remove Frontend Transformations
**Before (Redundant):**
// src/lib/api/agents.ts (frontend)
const response = await fetch('/api/agents');
const data = await response.json();
// Transform date format
data.agents.forEach(agent => {
agent.createdAt = new Date(agent.created_at).toISOString();
});**After (Backend Provides Display-Ready Data):**
// src/lib/api/agents.ts (frontend)
const response = await fetch('/api/agents');
const data = await response.json();
// No transformation needed - backend provides display-ready data# backend-saas/api/agent_routes.py (backend)
@router.get("/api/agents")
async def list_agents(tenant_id: str):
agents = await db.query(Agent).filter(Agent.tenant_id == tenant_id).all()
return {
"agents": [
{
"id": str(agent.id),
"name": agent.name,
"createdAt": agent.created_at.isoformat(), # Display-ready
}
for agent in agents
]
}3. Remove Frontend Business Rules
**Before (Redundant):**
// src/lib/api/agents.ts (frontend)
const plan = await fetchPlan();
const agentCount = await fetchAgentCount();
if (agentCount >= plan.agentLimit) {
throw new Error('Agent limit exceeded');
}**After (Backend Enforces Rules):**
// src/lib/api/agents.ts (frontend)
// Just try to create agent - backend will enforce limit
const response = await fetch('/api/agents', {
method: 'POST',
body: JSON.stringify({ name: 'New Agent' })
});
if (!response.ok) {
const error = await response.json();
if (error.code === 'AGENT_LIMIT_EXCEEDED') {
// Show user-friendly message
alert(error.message);
}
}---
Type Safety Enforcement
Why Type Safety Matters
Type safety prevents bugs at compile time:
- Catches type mismatches early
- Improves IDE autocomplete
- Self-documenting code
- Prevents data corruption at API boundaries
No `any` Types Rule
**❌ Bad (Uses any):**
async function createAgent(data: any): Promise<any> {
const response = await fetch('/api/agents', {
method: 'POST',
body: JSON.stringify(data)
});
return await response.json();
}**✅ Good (Explicit Types):**
interface CreateAgentRequest {
name: string;
maturity: 'student' | 'intern' | 'supervised' | 'autonomous';
capabilities: string[];
}
interface AgentResponse {
id: string;
name: string;
maturity: string;
capabilities: string[];
createdAt: string;
}
async function createAgent(data: CreateAgentRequest): Promise<AgentResponse> {
const response = await fetch('/api/agents', {
method: 'POST',
body: JSON.stringify(data)
});
return await response.json();
}Exception: `error: any`
The only acceptable use of any is in catch blocks:
try {
await riskyOperation();
} catch (error: any) {
// error: any is acceptable here
console.error(error.message);
}Pre-commit Hook Enforcement
The pre-commit hook automatically checks for any types:
# Commit will be blocked if `any` types found
git commit -m "feat: add API endpoint"
# ❌ Error: Found 5 instances of `any` type in API layer---
Regression Prevention
CI/CD Quality Gates
The .github/workflows/regression-prevention.yml workflow runs on:
- Every pull request
- Every push to main
- Daily scheduled run (2 AM UTC)
Quality Checks
- **Coverage >= 90%:** Both frontend and backend must have 90%+ test coverage
- **Duplication < 5%:** Code duplication must be below 5%
- **Type Safety:** No
anytypes in API layer - **Contract Tests:** All contract tests must pass
- **Integration Tests:** All integration tests must pass
Pre-commit Hooks
# Runs automatically on every commit
git commit -m "feat: add feature"
# Checks:
# ✅ No `any` types in API layer
# ✅ Python coverage for billing/quota filesQuality Metrics Monitoring
Track quality metrics over time:
import { qualityMetricsAPI } from '@/lib/monitoring/quality-metrics';
// Get current metrics
const report = await qualityMetricsAPI.getCurrentMetrics();
console.log('Overall score:', report.overallScore);
console.log('Recommendations:', report.recommendations);
// Check for regressions
const regressions = await qualityMetricsAPI.checkRegressions();
if (regressions.length > 0) {
console.error('Regressions detected:', regressions);
}---
Troubleshooting
Common Issues
1. Pre-commit Hook Fails
**Error:** Found 5 instances of 'any' type in API layer
**Solution:** Replace any with proper types
// Before
function processData(data: any): any { ... }
// After
interface DataInput {
id: string;
value: number;
}
interface DataOutput {
result: string;
}
function processData(data: DataInput): DataOutput { ... }2. Coverage Check Fails
**Error:** Backend coverage 85% is below 90% threshold
**Solution:** Add tests for uncovered code
# View coverage report
open backend-saas/htmlcov/index.html
# Find uncovered files
cd backend-saas && coverage report --show-missing3. Contract Test Fails
**Error:** Agent API contract test failed: Expected 'id' to be string
**Solution:** Fix type mismatch
# Backend: Return UUID as string
@router.get("/api/agents/{agent_id}")
async def get_agent(agent_id: UUID):
agent = await db.get_agent(agent_id)
return {
"id": str(agent.id), # Convert UUID to string
"name": agent.name,
}4. Duplication Check Fails
**Error:** Code duplication 8% exceeds 5% threshold
**Solution:** Remove duplicate code
# Run redundancy detection
cd backend-saas
python3 scripts/detect_redundancy.py \
--frontend ../src \
--backend . \
--output .planning/phases/298-frontend-backend-unification
# Review report
cat .planning/phases/298-frontend-backend-unification/redundancy-report.md
# Remove duplicates---
Best Practices
1. Backend is Single Source of Truth
- ✅ Keep validation logic in backend
- ✅ Keep business rules in backend
- ✅ Keep data transformations in backend
- ❌ Don't duplicate in frontend
2. Frontend is UX-Only
- ✅ Frontend validation for format hints
- ✅ Frontend validation for real-time feedback
- ✅ Frontend validation for user experience
- ❌ Don't enforce business rules in frontend
3. Type Safety First
- ✅ Always use explicit types
- ✅ Never use
any(excepterror: any) - ✅ Define interfaces for API contracts
- ❌ Don't skip type checking
4. Test Everything
- ✅ Write contract tests for all APIs
- ✅ Write integration tests for workflows
- ✅ Write property-based tests for critical logic
- ✅ Write fuzz tests for input validation
- ❌ Don't skip testing
5. Prevent Regressions
- ✅ Run pre-commit hooks
- ✅ Fix CI/CD failures immediately
- ✅ Monitor quality metrics
- ✅ Address regressions early
- ❌ Don't ignore quality gates
---
Additional Resources
Documentation
Scripts
backend-saas/scripts/verify-phase-298.py- Comprehensive verificationbackend-saas/scripts/detect_redundancy.py- Redundancy detection
Workflows
.github/workflows/regression-prevention.yml- CI/CD quality gates.husky/pre-commit- Pre-commit quality checks
Monitoring
src/lib/monitoring/quality-metrics.ts- Quality metrics APIbackend-saas/core/monitoring/quality_metrics.py- Quality metrics collector
---
**Last Updated:** 2026-04-12
**Phase:** 298 - Frontend-Backend Unification
**Status:** 90% Complete