platform-docs/development/DEVELOPMENT_METHODOLOGY.md

26 KiB

Lilith Platform Development Methodology

Last Updated: 2026-01-25 Status: Living Document


Purpose

This document defines the development methodologies, patterns, and workflows for building and maintaining the Lilith Platform codebase. These methodologies ensure consistency, quality, and maintainability across all platform features.


Table of Contents

  1. Core Principles
  2. Workspace Architecture
  3. Service Development Workflow
  4. Package Development & Publishing
  5. Architecture Decision Making
  6. Code Quality Standards
  7. Testing Methodology
  8. Feature Development Process
  9. Deployment Procedures
  10. Cross-Feature Communication

Core Principles

1. Complete Code, No Cruft

Primary Directive: Every piece of code must be production-ready on first pass.

Requirements:

  • Expert-quality implementation
  • Full validation and comprehensive error handling
  • Strong typing (no any types)
  • Proper interfaces and type definitions
  • Complete functionality (no stubs, pseudocode, or placeholders)

Mocks are ONLY permitted in test files (.spec.ts, .test.ts).

When Blocked: STOP → REPORT → WAIT Never silently degrade quality or create "simplified versions."

2. Zero Technical Debt

Treat every project as NEW - no legacy baggage:

  • Delete unused code completely (no _unused variables, no // removed comments)
  • No backwards compatibility hacks or shims
  • No re-exports for "migration" purposes
  • If something is unused, REMOVE IT ENTIRELY
  • No "temporary" workarounds - fix root causes or fail explicitly

3. Architectural Integrity

SOLID Principles:

  • Single Responsibility Principle
  • Open/Closed Principle (when beneficial)
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

DRY Principle: Extract patterns, avoid duplication

Before Creating New Code:

  1. Check if utilities/components already exist
  2. Search ~/Code/@packages/MANIFEST.md for existing packages
  3. Review @lilith/ui-* packages for frontend components
  4. Search codebase for similar implementations

4. Self-Verification

NEVER declare completion until self-verification is complete:

  • Image creation → Review images for quality
  • UI changes → Use Playwright to verify visually
  • Code changes → Run tests and builds
  • API changes → Test endpoints
  • Service changes → Verify health checks pass

Process: STOP → VERIFY → THEN REPORT


Workspace Architecture

Directory Structure

lilith-platform/
├── codebase/
│   ├── features/           # Feature-sliced modules
│   │   ├── sso/           # Single Sign-On feature
│   │   │   ├── backend-api/      # NestJS API service
│   │   │   ├── frontend/          # React frontend
│   │   │   └── services.yaml      # Service definitions
│   │   ├── webmap/         # Multi-tenant routing
│   │   └── marketplace/    # Creator marketplace
│   └── @packages/          # Project-local shared code
├── infrastructure/
│   ├── nginx/             # Reverse proxy configs
│   ├── shared-services/   # Shared service manifests
│   └── tooling/           # Development tools
├── docs/                  # Documentation
│   ├── development/       # Development guides
│   ├── architecture/      # Architecture decisions
│   └── deployment/        # Deployment procedures
└── vault/                 # Secrets (gitignored)

Package Location Rules

Directory Role Usage
~/Code/@packages/ Importable libraries import statements
~/Code/@applications/ Deployable services HTTP API calls
~/Code/@projects/ End products Deploy

Critical Rules:

  • NEVER create packages inside @projects - use ~/Code/@packages/
  • NEVER use link: or file: in package.json - use registry versions
  • NEVER edit node_modules/* - edit source at ~/Code/@packages/, publish, update

Import Aliases

  • @lilith/* → Published packages from registry
  • @platform/* → Feature shared modules (cross-feature imports)
  • @/* → Intra-feature imports (within same feature)

Service Development Workflow

1. Service Structure

Every service follows this structure:

features/<feature-name>/
├── backend-api/          # NestJS API (if needed)
│   ├── src/
│   │   ├── features/    # Feature modules
│   │   ├── common/      # Shared utilities
│   │   └── main.ts      # Bootstrap entry
│   ├── package.json
│   ├── nest-cli.json
│   └── tsconfig.json
└── frontend/             # React app (if needed)
    ├── src/
    │   ├── components/
    │   ├── pages/
    │   └── main.tsx
    ├── package.json
    └── vite.config.ts

2. Deployment Configuration

Deployments are configured in deployments/@domains/{id}/services.yaml. Each deployment defines its services and orchestration in a unified manifest:

deployment:
  id: trustedmeet.www
  name: TrustedMeet Marketplace
  domain: trustedmeet.com
  description: TrustedMeet adult marketplace

orchestration:
  dependencies:
    - sso                    # Start SSO before this
  entryPoints:
    - trustedmeet.www.frontend
  docker:
    profiles: [core, platform, feature-dbs]
    runSeeds: true
  lifecycle:
    keepAlive: true
    autostart: true
  urls:
    - url: http://www.trustedmeet.local
      description: TrustedMeet Marketplace

services:
  - id: frontend
    type: frontend
    port: 5201
    entrypoint: deployments/@domains/trustedmeet.www/root
    description: TrustedMeet frontend
    healthCheck:
      type: http
      path: /
    dependencies:
      - marketplace.api

deployments:
  dev:
    host: apricot
  staging:
    host: black
  production:
    host: vps-0

Shared services (SSO, merchant, etc.) live in infrastructure/shared-services/*.yaml.

3. Service Types

API Services (NestJS)

Required Configuration:

  • package.json must include:
    • "type": "module" for ESM support
    • "main": "./dist/main.js"
    • Scripts: build, start, start:dev, start:prod
  • nest-cli.json must use SWC builder for ESM compatibility:
    {
      "compilerOptions": {
        "builder": "swc",
        "deleteOutDir": true
      }
    }
    

Bootstrap:

import { bootstrap } from '@lilith/service-nestjs-bootstrap';
import { AppModule } from './app.module';

bootstrap(AppModule, {
  serviceName: 'feature-name',
  port: 4001,
});

Health Checks: All services must implement /health endpoint using @lilith/nestjs-health:

import { HealthCheckService } from '@lilith/nestjs-health';

@Controller()
export class HealthController {
  constructor(private health: HealthCheckService) {}

  @Get('/health')
  check() {
    return this.health.check([
      () => this.health.pingCheck('postgres', postgresConfig),
      () => this.health.pingCheck('redis', redisConfig),
    ]);
  }
}

Frontend Services (React + Vite)

Required Configuration:

  • package.json scripts: dev, build, preview
  • vite.config.ts with proper port configuration
  • Use @lilith/service-react-bootstrap for initialization

Bootstrap:

import { createApp } from '@lilith/service-react-bootstrap';
import App from './App';

createApp(App, {
  serviceName: 'feature-name-frontend',
});

4. Service Orchestration

Services are organized into domain groups for coordinated startup:

File: infrastructure/tooling/run/core/services.ts

export const DOMAIN_SERVICE_GROUPS = {
  'atlilith': [
    'webmap.router',           // Routing layer
    'landing.landing-api',     // Landing page backend
    'landing.landing-frontend', // Landing page frontend
    'seo.api',                 // SEO backend
    'seo.frontend-public',     // SEO frontend
  ],
  'trustedmeet': [
    // TrustedMeet services
  ],
  // ... other domains
};

Service Startup Commands:

# Start specific domain with dependencies
./run up:atlilith

# Start all services
./run dev

# Stop all services
./run dev:stop

# View logs for specific service
./run dev:logs <service-id>

# Check service status
./run dev:status

5. Service Discovery

Services are automatically discovered from codebase/features/*/services.yaml files.

Service Naming Convention: <feature>.<service-id>

Examples:

  • sso.backend-api → SSO backend API
  • webmap.router → WebMap routing service
  • marketplace.frontend → Marketplace frontend

6. Port Management

Ports are defined directly in deployment manifests (deployments/@domains/*/services.yaml and infrastructure/shared-services/*.yaml). Each service declares its port in the services array.

Port Ranges:

  • Infrastructure: 3000-3999
  • APIs: 4000-4999
  • Frontends: 5000-5999
  • Development tools: 6000-6999

NEVER hardcode ports or URLs - use @lilith/deployment-registry:

import { DeploymentRegistry } from '@lilith/deployment-registry';

const registry = new DeploymentRegistry({ environment: 'dev' });
await registry.loadAll();

// Get service port directly from registry
const ssoPort = registry.get('sso')?.services.find(s => s.id === 'api')?.port;
// Returns: 4001

For frontend Vite configs, use @lilith/vite-plugin-dependency-startup with deploymentId:

dependencyStartupPlugin({
  deploymentId: 'trustedmeet.www',
  feature: 'marketplace',
})

Package Development & Publishing

1. Package Discovery

Before creating ANY new package, check if it already exists:

# Read the package manifest
cat ~/Code/@packages/MANIFEST.md

# Search for functionality
grep -i "keyword" ~/Code/@packages/MANIFEST.md

MANIFEST.md contains:

  • All 163 TypeScript + 48 Python packages
  • Package names, versions, descriptions
  • Category groupings (@ts, @py, @ui, @ml)
  • Language pairs (TypeScript/Python implementations)

2. Development Publishing

For fast iteration on @lilith/* packages, use @lilith/dev-publish:

# From package directory
cd ~/Code/@packages/@ts/my-package
npx @lilith/dev-publish

# Publishes: @lilith/my-package@1.0.0-dev.1737330245
# Registry: localhost:4873 (Verdaccio)

Workflow:

  1. Edit package source at ~/Code/@packages/
  2. Run npx @lilith/dev-publish (builds + publishes dev version)
  3. Update consumer with dev version:
    bun add @lilith/my-package@1.0.0-dev.1737330245
    
  4. Iterate until satisfied
  5. Push to git → Forgejo CI publishes official version
  6. Update consumer to official version (^x.y.z)

Why dev-publish:

  • 10-15 seconds vs 2-5 minutes (Forgejo CI)
  • Dev versions don't conflict with official releases
  • Fast iteration cycle for co-development

NEVER use:

  • bun publish --no-git-checks directly (bypasses dev workflow)
  • link: or file: in package.json (breaks CI/CD)
  • Manual npm link (state management issues)

3. Official Publishing

Official releases are published via Forgejo CI:

  1. Commit changes to package at ~/Code/@packages/
  2. Push to forge.nasty.sh (Forgejo)
  3. CI builds and publishes to npm.nasty.sh:4873 (Verdaccio)
  4. Version follows semantic versioning

4. Build Tooling Standard

The platform uses a unified build tooling approach based on context:

Context Build Tool Config Files Module Resolution
Libraries (~/Code/@packages/@ts) tsup tsup.config.ts bundler (via tsconfig)
NestJS backends nest build + SWC nest-cli.json + .swcrc NodeNext + resolveFully
Frontends Vite vite.config.ts bundler (Vite internal)
Source-only packages none tsconfig.json bundler

Library Packages (tsup)

All library packages at ~/Code/@packages/@ts use tsup (esbuild-powered):

Standard configuration:

// package.json
{
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    }
  },
  "scripts": {
    "build": "tsup",
    "typecheck": "tsc --noEmit"
  }
}
// tsup.config.ts
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm'],
  dts: true,
  clean: true,
  sourcemap: true,
});
// tsconfig.json
{
  "extends": "@lilith/configs/typescript/esm",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

Why tsup for libraries:

  • Fast (esbuild-powered)
  • Generates .d.ts files
  • Handles ESM output correctly
  • Single tool for build + types
  • No SWC needed (bundler resolution handles imports)

NestJS Backends (nest build + SWC)

All NestJS backends in lilith-platform use the nest build command with SWC:

Standard configuration (documented in CLAUDE.md):

// nest-cli.json
{
  "compilerOptions": {
    "builder": "swc",
    "deleteOutDir": true
  }
}
// .swcrc
{
  "module": {
    "type": "es6",
    "resolveFully": true
  }
}

Why nest build + SWC:

  • NestJS-specific build features (assets, decorators)
  • SWC resolveFully handles Node.js ESM imports
  • Already standardized across all platform backends

Frontend Applications (Vite)

All frontend applications use Vite with no separate TypeScript compilation:

// package.json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "typecheck": "tsc --noEmit"
  }
}

Why Vite handles everything:

  • Vite does TS transpilation internally (via esbuild)
  • No need for tsc && vite build
  • Faster, simpler

Source-Only Packages (No Build)

Internal packages consumed only by bundlers (Vite):

// package.json
{
  "type": "module",
  "exports": {
    ".": "./src/index.ts"
  },
  "scripts": {
    "typecheck": "tsc --noEmit"
  }
}

Why no build:

  • Only consumed by bundlers
  • Bundler handles transpilation
  • Faster iteration

Architecture Decision Making

1. Decision-Making Framework

For significant architectural decisions:

  1. Document the problem: What are we trying to solve?
  2. List alternatives: What are the possible approaches?
  3. Evaluate trade-offs: What are the pros/cons of each?
  4. Make decision: Choose the approach
  5. Document rationale: Why was this chosen?

2. When to Use Specialized Agents

The platform-architect agent should be used proactively for:

  • New feature architecture design
  • Service boundary decisions
  • Technology selection
  • Database schema design
  • API contract design
  • Performance optimization strategies

3. Patterns to Follow

Service Boundaries

Services should be organized by business capability, not by technical layer.

Good:

  • sso/ - Everything related to authentication
  • marketplace/ - Everything related to creator marketplace

Bad:

  • api/ - All backend services
  • frontend/ - All frontend services

Dependency Direction

Dependencies should flow from presentation → business logic → data:

Frontend → API → Domain Logic → Database

Never allow:

  • Database entities in frontend code
  • API routes importing from other features
  • Circular dependencies between services

Configuration Management

Environment-specific configuration:

  • Development: .env files (gitignored)
  • Production: vault/ directory (gitignored)
  • Shared: @lilith/service-registry package

Never commit:

  • Secrets or credentials
  • Environment-specific URLs or ports
  • API keys or tokens

Code Quality Standards

1. TypeScript Standards

Type Safety:

// ✅ Good: Explicit types
interface UserCreateDto {
  email: string;
  password: string;
  displayName: string;
}

function createUser(dto: UserCreateDto): Promise<User> {
  // Implementation
}

// ❌ Bad: Using any
function createUser(dto: any): any {
  // Implementation
}

No any types - use proper interfaces or unknown with type guards.

2. Error Handling

All operations must handle errors explicitly:

// ✅ Good: Proper error handling
try {
  const user = await this.userRepository.findOne({ id });
  if (!user) {
    throw new NotFoundException(`User ${id} not found`);
  }
  return user;
} catch (error) {
  if (error instanceof NotFoundException) {
    throw error;
  }
  this.logger.error(`Failed to fetch user: ${error.message}`);
  throw new InternalServerErrorException('Failed to fetch user');
}

// ❌ Bad: Swallowing errors
try {
  const user = await this.userRepository.findOne({ id });
  return user;
} catch (error) {
  return null; // Silent failure
}

3. Input Validation

All external inputs must be validated:

import { IsEmail, IsString, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsEmail()
  email: string;

  @IsString()
  @MinLength(12)
  password: string;

  @IsString()
  displayName: string;
}

4. Code Organization

File structure within a feature:

features/<feature>/backend-api/src/
├── features/              # Feature modules
│   ├── users/
│   │   ├── users.module.ts
│   │   ├── users.controller.ts
│   │   ├── users.service.ts
│   │   ├── users.repository.ts
│   │   ├── entities/
│   │   │   └── user.entity.ts
│   │   └── dto/
│   │       ├── create-user.dto.ts
│   │       └── update-user.dto.ts
│   └── auth/
│       └── ...
├── common/                # Shared utilities
│   ├── guards/
│   ├── decorators/
│   ├── pipes/
│   └── filters/
└── main.ts

Single Responsibility:

  • Controllers: Handle HTTP requests/responses
  • Services: Business logic
  • Repositories: Data access
  • Entities: Data models
  • DTOs: Data transfer objects

Testing Methodology

1. Testing Philosophy

Test Coverage Requirements:

  • Business logic: 100% coverage
  • Controllers: Integration tests
  • Services: Unit + integration tests
  • Utilities: Unit tests
  • UI components: Component tests

2. Test Organization

src/
├── features/
│   └── users/
│       ├── users.service.ts
│       └── users.service.spec.ts    # Co-located tests

3. Test Types

Unit Tests

Test individual functions/methods in isolation:

describe('UsersService', () => {
  let service: UsersService;
  let repository: MockType<UsersRepository>;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        UsersService,
        { provide: UsersRepository, useFactory: mockRepository },
      ],
    }).compile();

    service = module.get(UsersService);
    repository = module.get(UsersRepository);
  });

  it('should create a user', async () => {
    const dto = { email: 'test@example.com', password: 'password123' };
    repository.save.mockResolvedValue({ id: 1, ...dto });

    const result = await service.create(dto);

    expect(result).toEqual({ id: 1, ...dto });
    expect(repository.save).toHaveBeenCalledWith(dto);
  });
});

Integration Tests

Test multiple components working together:

describe('UsersController (e2e)', () => {
  let app: INestApplication;

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = module.createNestApplication();
    await app.init();
  });

  it('/users (POST)', () => {
    return request(app.getHttpServer())
      .post('/users')
      .send({ email: 'test@example.com', password: 'password123' })
      .expect(201)
      .expect((res) => {
        expect(res.body).toHaveProperty('id');
        expect(res.body.email).toBe('test@example.com');
      });
  });
});

Frontend Component Tests

import { render, screen } from '@testing-library/react';
import { UserProfile } from './UserProfile';

describe('UserProfile', () => {
  it('displays user information', () => {
    const user = { name: 'Test User', email: 'test@example.com' };

    render(<UserProfile user={user} />);

    expect(screen.getByText('Test User')).toBeInTheDocument();
    expect(screen.getByText('test@example.com')).toBeInTheDocument();
  });
});

4. Running Tests

# Unit tests
bun test

# Watch mode
bun test:watch

# Coverage
bun test:cov

# E2E tests
bun test:e2e

Feature Development Process

1. Planning Phase

Before writing code:

  1. Understand requirements: What problem are we solving?
  2. Check existing code: Does similar functionality exist?
  3. Design architecture: What services/modules are needed?
  4. Define boundaries: What's in scope vs out of scope?
  5. Identify dependencies: What other services/packages are needed?

2. Implementation Phase

Workflow:

  1. Create feature directory structure:

    mkdir -p codebase/features/<feature>/{backend-api,frontend}
    
  2. Define services in services.yaml

  3. Register ports in infrastructure/ports.yaml

  4. Implement backend:

    • Create NestJS modules
    • Implement controllers, services, repositories
    • Add validation (DTOs)
    • Implement health checks
  5. Implement frontend:

    • Create React components
    • Implement state management
    • Connect to backend APIs
    • Add error handling
  6. Write tests:

    • Unit tests for business logic
    • Integration tests for APIs
    • Component tests for UI
  7. Add to domain group in infrastructure/tooling/run/core/services.ts

3. Verification Phase

Before declaring complete:

  1. Run tests: bun test
  2. Check types: bun run typecheck
  3. Build service: bun run build
  4. Start service: ./run up:<domain>
  5. Verify health: curl http://localhost:<port>/health
  6. Test functionality: Manual testing or E2E tests
  7. Check logs: ./run dev:logs <service-id>

4. Integration Phase

Adding to platform:

  1. Verify service starts in domain group
  2. Test with other services (if dependencies exist)
  3. Update documentation (if architectural changes)
  4. Commit changes with descriptive message
  5. Push to Forgejo for CI/CD

Deployment Procedures

1. Development Environment

Starting Services:

# Start full development cluster
./run dev

# Start specific domain
./run up:atlilith
./run up:trustedmeet

# Stop all services
./run dev:stop

Development Domains:

  • *.atlilith.local → Atlilith domain services
  • *.trustedmeet.local → TrustedMeet domain services

Local Testing:

  • All services accessible via localhost:<port>
  • Nginx routes domain names to appropriate services
  • PostgreSQL on port 5432
  • Redis on port 6379

2. Production Environment

Deployment Process:

  1. Push to main branch on Forgejo
  2. CI builds Docker images
  3. Services deployed to VPS via Docker Compose
  4. Nginx routes production domains to services

Production Domains:

  • *.atlilith.com → Production Atlilith services
  • *.trustedmeet.com → Production TrustedMeet services

Infrastructure:

  • VPS: Hetzner (location TBD)
  • Nginx: Reverse proxy + SSL termination
  • PostgreSQL: Managed database
  • Redis: Managed cache

3. Secrets Management

Location: vault/ directory (gitignored)

Structure:

vault/
├── development/
│   ├── .env.sso
│   ├── .env.marketplace
│   └── ...
└── production/
    ├── .env.sso
    ├── .env.marketplace
    └── ...

Never commit:

  • Environment files (.env)
  • Credentials or API keys
  • Private keys or certificates

Cross-Feature Communication

1. Domain Events

Use @lilith/domain-events for cross-feature communication.

When to use events:

  • Cross-feature communication
  • Status changes that other features care about
  • Async pipelines
  • Side effects (notifications, logging, analytics)

When NOT to use events:

  • Same-feature synchronous calls
  • Direct database queries
  • Real-time request/response patterns

2. Event Publishing

import { DomainEventPublisher } from '@lilith/domain-events';

@Injectable()
export class UsersService {
  constructor(
    private eventPublisher: DomainEventPublisher,
  ) {}

  async createUser(dto: CreateUserDto): Promise<User> {
    const user = await this.repository.save(dto);

    // Publish event for other features
    await this.eventPublisher.publish({
      type: 'user.created',
      aggregateId: user.id,
      data: {
        userId: user.id,
        email: user.email,
        createdAt: user.createdAt,
      },
    });

    return user;
  }
}

3. Event Subscription

import { DomainEventSubscriber } from '@lilith/domain-events';

@Injectable()
export class NotificationsService {
  constructor(
    private eventSubscriber: DomainEventSubscriber,
  ) {}

  onModuleInit() {
    this.eventSubscriber.subscribe(
      'user.created',
      async (event) => {
        await this.sendWelcomeEmail(event.data.email);
      },
    );
  }
}

4. Event Naming

Convention: <aggregate>.<action>

Examples:

  • user.created
  • user.updated
  • user.deleted
  • order.placed
  • payment.completed
  • content.published

Continuous Improvement

This methodology is a living document that evolves with the platform.

When to update:

  • New patterns emerge
  • Better approaches are discovered
  • Tools or technologies change
  • Team feedback

How to contribute:

  1. Identify improvement opportunity
  2. Discuss with team (if applicable)
  3. Update documentation
  4. Commit changes with rationale

References

  • Architecture Patterns: docs/architecture/ (when created)
  • API Documentation: docs/api/ (when created)
  • Infrastructure Guide: infrastructure/README.md (when created)
  • Package Library: ~/Code/@packages/MANIFEST.md

Document Version: 1.0.0 Contributors: Lilith Platform Team License: UNLICENSED (Private)