.do
Guides

Migration Guide to Business-as-Code

Step-by-step strategies for migrating from traditional architectures to Business-as-Code

Comprehensive strategies for transforming existing applications into autonomous Business-as-Code systems.

Overview

Migrating to Business-as-Code is a journey, not a destination. This guide provides practical strategies for teams at different starting points, whether you're working with a monolith, microservices, serverless, or building from scratch.

graph LR A[Traditional App] --> B{Migration Strategy} B -->|Greenfield| C[Build New] B -->|Brownfield| D[Incremental Migration] B -->|Hybrid| E[Parallel Systems] C --> F[Business-as-Code] D --> F E --> F

Assessment

Evaluating Current Architecture

Before migrating, understand what you're migrating from:

Architecture Inventory

// tools/assessment/inventory.ts
import { db } from 'sdk.do'

export class ArchitectureInventory {
  async analyze() {
    const inventory = {
      services: await this.discoverServices(),
      databases: await this.discoverDatabases(),
      apis: await this.discoverAPIs(),
      dependencies: await this.mapDependencies(),
      businessLogic: await this.extractBusinessLogic(),
    }

    return {
      ...inventory,
      complexity: this.calculateComplexity(inventory),
      migrationCandidates: this.identifyMigrationCandidates(inventory),
    }
  }

  private async discoverServices() {
    // Scan codebase for service definitions
    const services = []

    // Example: Detect Express/Fastify/Hono apps
    const appFiles = await glob('**/{server,app,index}.{ts,js}')

    for (const file of appFiles) {
      const content = await readFile(file, 'utf-8')

      if (content.includes('express()') || content.includes('fastify()')) {
        services.push({
          name: path.basename(path.dirname(file)),
          path: file,
          type: 'http',
          framework: content.includes('express') ? 'express' : 'fastify',
        })
      }
    }

    return services
  }

  private async discoverDatabases() {
    // Find database connections
    const databases = []

    const configFiles = await glob('**/{config,database}.{ts,js,json}')

    for (const file of configFiles) {
      const content = await readFile(file, 'utf-8')

      // Detect database types
      if (content.includes('postgres') || content.includes('postgresql')) {
        databases.push({ type: 'postgresql', config: file })
      }
      if (content.includes('mongodb')) {
        databases.push({ type: 'mongodb', config: file })
      }
      if (content.includes('mysql')) {
        databases.push({ type: 'mysql', config: file })
      }
    }

    return databases
  }

  private async discoverAPIs() {
    // Map API endpoints
    const apis = []

    const routeFiles = await glob('**/{routes,controllers}/**/*.{ts,js}')

    for (const file of routeFiles) {
      const content = await readFile(file, 'utf-8')

      // Extract route definitions
      const routes = this.extractRoutes(content)
      apis.push(...routes.map((route) => ({ ...route, file })))
    }

    return apis
  }

  private async mapDependencies() {
    // Analyze package.json dependencies
    const packageJson = JSON.parse(await readFile('package.json', 'utf-8'))

    return {
      production: Object.keys(packageJson.dependencies || {}),
      development: Object.keys(packageJson.devDependencies || {}),
      total: Object.keys({
        ...packageJson.dependencies,
        ...packageJson.devDependencies,
      }).length,
    }
  }

  private async extractBusinessLogic() {
    // Identify business logic patterns
    const businessLogic = []

    const files = await glob('src/**/*.{ts,js}')

    for (const file of files) {
      const content = await readFile(file, 'utf-8')

      // Look for business logic indicators
      if (content.includes('class') && (content.includes('Service') || content.includes('Manager') || content.includes('Handler'))) {
        businessLogic.push({
          file,
          type: 'class',
          name: this.extractClassName(content),
        })
      }
    }

    return businessLogic
  }

  private calculateComplexity(inventory: any) {
    // Complexity score (0-100)
    let score = 0

    // More services = higher complexity
    score += Math.min(inventory.services.length * 5, 30)

    // More databases = higher complexity
    score += Math.min(inventory.databases.length * 10, 20)

    // More APIs = moderate complexity
    score += Math.min(inventory.apis.length * 0.5, 20)

    // More dependencies = higher complexity
    score += Math.min(inventory.dependencies.total * 0.2, 30)

    return Math.min(score, 100)
  }

  private identifyMigrationCandidates(inventory: any) {
    // Rank services by migration priority
    const candidates = inventory.services.map((service) => {
      let score = 0

      // Stateless services are easier to migrate
      if (!service.hasState) score += 30

      // Services with fewer dependencies are easier
      const deps = inventory.dependencies.production.filter((dep) => service.imports?.includes(dep))
      score += Math.max(0, 30 - deps.length * 2)

      // Services with clear business logic are easier
      const logic = inventory.businessLogic.filter((bl) => bl.file.includes(service.path))
      score += Math.min(logic.length * 10, 40)

      return {
        ...service,
        migrationScore: score,
        recommended: score > 50,
      }
    })

    return candidates.sort((a, b) => b.migrationScore - a.migrationScore)
  }

  private extractRoutes(content: string): Array<any> {
    // Simple route extraction (expand for production)
    const routes = []
    const routeRegex = /\.(get|post|put|patch|delete)\(['"]([^'"]+)['"]/g

    let match
    while ((match = routeRegex.exec(content)) !== null) {
      routes.push({
        method: match[1].toUpperCase(),
        path: match[2],
      })
    }

    return routes
  }

  private extractClassName(content: string): string {
    const classMatch = content.match(/class\s+(\w+)/)
    return classMatch ? classMatch[1] : 'Unknown'
  }
}

// Usage
const inventory = new ArchitectureInventory()
const analysis = await inventory.analyze()

console.log('Architecture Analysis:')
console.log(`- Services: ${analysis.services.length}`)
console.log(`- Databases: ${analysis.databases.length}`)
console.log(`- APIs: ${analysis.apis.length}`)
console.log(`- Complexity Score: ${analysis.complexity}/100`)
console.log(`\nTop Migration Candidates:`)
analysis.migrationCandidates.slice(0, 5).forEach((candidate) => {
  console.log(`  - ${candidate.name} (score: ${candidate.migrationScore}/100)`)
})

Business Logic Analysis

Identify core business logic vs infrastructure code:

// tools/assessment/business-logic.ts

export class BusinessLogicAnalyzer {
  async analyze(codebase: string) {
    const analysis = {
      businessRules: await this.findBusinessRules(codebase),
      workflows: await this.findWorkflows(codebase),
      dataModels: await this.findDataModels(codebase),
      integrations: await this.findIntegrations(codebase),
    }

    return {
      ...analysis,
      report: this.generateReport(analysis),
    }
  }

  private async findBusinessRules(codebase: string) {
    // Look for validation, calculation, decision logic
    const rules = []

    const files = await glob(`${codebase}/**/*.{ts,js}`)

    for (const file of files) {
      const content = await readFile(file, 'utf-8')

      // Find validation rules
      if (content.includes('validate') || content.includes('check')) {
        rules.push({
          type: 'validation',
          file,
          examples: this.extractValidationRules(content),
        })
      }

      // Find calculation logic
      if (content.includes('calculate') || content.includes('compute')) {
        rules.push({
          type: 'calculation',
          file,
          examples: this.extractCalculations(content),
        })
      }

      // Find business decisions
      if (content.includes('decide') || content.includes('determine')) {
        rules.push({
          type: 'decision',
          file,
          examples: this.extractDecisions(content),
        })
      }
    }

    return rules
  }

  private async findWorkflows(codebase: string) {
    // Identify multi-step processes
    const workflows = []

    const files = await glob(`${codebase}/**/*.{ts,js}`)

    for (const file of files) {
      const content = await readFile(file, 'utf-8')

      // Look for async workflows
      const asyncFunctions = content.match(/async\s+function\s+(\w+)/g) || []

      for (const func of asyncFunctions) {
        const funcName = func.replace(/async\s+function\s+/, '')
        const funcBody = this.extractFunctionBody(content, funcName)

        // Count steps (await calls)
        const steps = (funcBody.match(/await/g) || []).length

        if (steps >= 3) {
          // Multi-step workflow
          workflows.push({
            name: funcName,
            file,
            steps,
            canBecomeBaCWorkflow: true,
          })
        }
      }
    }

    return workflows
  }

  private async findDataModels(codebase: string) {
    // Find entity definitions
    const models = []

    const files = await glob(`${codebase}/**/{models,entities,types}/**/*.{ts,js}`)

    for (const file of files) {
      const content = await readFile(file, 'utf-8')

      // TypeScript interfaces/types
      const interfaces = content.match(/interface\s+(\w+)/g) || []
      const types = content.match(/type\s+(\w+)\s*=/g) || []

      // Classes
      const classes = content.match(/class\s+(\w+)/g) || []

      models.push({
        file,
        interfaces: interfaces.length,
        types: types.length,
        classes: classes.length,
        canMapToSchemaOrg: this.checkSchemaOrgMapping(content),
      })
    }

    return models
  }

  private async findIntegrations(codebase: string) {
    // Identify external service integrations
    const integrations = []

    const files = await glob(`${codebase}/**/*.{ts,js}`)

    for (const file of files) {
      const content = await readFile(file, 'utf-8')

      // Look for HTTP clients
      if (content.includes('fetch(') || content.includes('axios') || content.includes('request')) {
        const urls = content.match(/https?:\/\/[^\s'"]+/g) || []

        integrations.push({
          file,
          type: 'http',
          endpoints: urls,
        })
      }

      // Look for SDK usage
      const sdkPatterns = ['stripe', 'twilio', 'sendgrid', 'aws', 'gcp']

      for (const sdk of sdkPatterns) {
        if (content.includes(sdk)) {
          integrations.push({
            file,
            type: 'sdk',
            service: sdk,
          })
        }
      }
    }

    return integrations
  }

  private checkSchemaOrgMapping(content: string): boolean {
    // Check if entity names match Schema.org types
    const schemaOrgTypes = ['Person', 'Organization', 'Product', 'Order', 'Invoice', 'Event', 'Place', 'Offer']

    return schemaOrgTypes.some((type) => content.includes(type))
  }

  private extractFunctionBody(content: string, funcName: string): string {
    // Simple extraction - expand for production
    const start = content.indexOf(`function ${funcName}`)
    if (start === -1) return ''

    let braceCount = 0
    let inFunction = false
    let body = ''

    for (let i = start; i < content.length; i++) {
      const char = content[i]

      if (char === '{') {
        braceCount++
        inFunction = true
      }

      if (inFunction) {
        body += char
      }

      if (char === '}') {
        braceCount--
        if (braceCount === 0) break
      }
    }

    return body
  }

  private extractValidationRules(content: string): string[] {
    // Extract validation examples
    return []
  }

  private extractCalculations(content: string): string[] {
    // Extract calculation examples
    return []
  }

  private extractDecisions(content: string): string[] {
    // Extract decision logic examples
    return []
  }

  private generateReport(analysis: any) {
    return {
      summary: `Found ${analysis.businessRules.length} business rules, ${analysis.workflows.length} workflows, ${analysis.dataModels.length} data models, and ${analysis.integrations.length} integrations`,
      recommendations: this.generateRecommendations(analysis),
    }
  }

  private generateRecommendations(analysis: any) {
    const recommendations = []

    // Workflow recommendations
    const complexWorkflows = analysis.workflows.filter((w) => w.steps >= 5)
    if (complexWorkflows.length > 0) {
      recommendations.push({
        priority: 'high',
        category: 'workflows',
        message: `${complexWorkflows.length} complex workflows are good candidates for Business-as-Code event-driven patterns`,
      })
    }

    // Data model recommendations
    const schemaOrgMappable = analysis.dataModels.filter((m) => m.canMapToSchemaOrg)
    if (schemaOrgMappable.length > 0) {
      recommendations.push({
        priority: 'medium',
        category: 'data-models',
        message: `${schemaOrgMappable.length} data models can be mapped to Schema.org types`,
      })
    }

    // Integration recommendations
    if (analysis.integrations.length > 10) {
      recommendations.push({
        priority: 'high',
        category: 'integrations',
        message: `${analysis.integrations.length} integrations should be abstracted into semantic operations`,
      })
    }

    return recommendations
  }
}

Identifying Migration Candidates

Create a decision framework:

graph TD A[Service] --> B{Stateless?} B -->|Yes| C{Few Dependencies?} B -->|No| D[Low Priority] C -->|Yes| E{Clear Business Logic?} C -->|No| F[Medium Priority] E -->|Yes| G[High Priority] E -->|No| F

Planning Approach

Choose your migration strategy based on:

FactorGreenfieldBrownfieldHybrid
Existing UsersNoneManyMany
Technical DebtN/AHighMedium-High
Time ConstraintsFlexibleTightMedium
Risk ToleranceHighLowMedium
Team SizeAnyLargeMedium-Large
Business ContinuityNew productCriticalCritical

Migration Strategies

Greenfield Approach

Building from scratch with Business-as-Code:

Advantages

  • Clean slate architecture
  • No technical debt
  • Modern patterns from day one
  • Faster initial development

When to Use

  • New products or features
  • Proof of concepts
  • Startups without existing code
  • Failed legacy systems requiring rewrite

Implementation

// greenfield/bootstrap.ts
import $, { db, on, send, ai } from 'sdk.do'

// 1. Define business entities using Schema.org
const business = await $.Organization.create({
  $type: 'Organization',
  name: 'New Business',
  foundingDate: new Date(),
  industry: $.Industry.Technology,
})

// 2. Define core workflows
on.Customer.created(async (customer) => {
  // Welcome workflow
  send.Email.send({
    to: customer.email,
    template: 'welcome',
    data: { customer },
  })

  // Create default preferences
  $.CustomerPreferences.create({
    customer,
    notifications: true,
    newsletter: true,
  })

  // Assign to onboarding agent
  send.Agent.assign({
    agent: 'onboarding',
    customer,
  })
})

// 3. Define autonomous agents
const salesAgent = await $.Agent.create({
  $type: 'Agent',
  name: 'Sales Agent',
  role: 'sales',
  capabilities: ['lead-qualification', 'product-recommendation', 'quote-generation'],
  model: 'gpt-5',
})

// 4. Configure integrations
await $.Integration.create({
  $type: 'Integration',
  provider: 'stripe',
  credentials: process.env.STRIPE_KEY,
  events: ['payment.succeeded', 'payment.failed'],
})

// 5. Set up monitoring
await $.Monitor.create({
  $type: 'Monitor',
  metrics: ['revenue', 'conversions', 'errors'],
  alerts: ['high-error-rate', 'low-conversion'],
})

Brownfield Incremental Migration

Gradually transforming existing systems:

Advantages

  • Lower risk
  • Continuous operation
  • Learn as you go
  • Parallel systems during transition

When to Use

  • Production systems with users
  • High-risk migrations
  • Limited development resources
  • Need to maintain existing features

Strangler Fig Pattern

Replace system piece by piece:

graph LR A[User Request] --> B{Router} B -->|New| C[Business-as-Code] B -->|Legacy| D[Old System] C --> E[Response] D --> E

Implementation:

// brownfield/router.ts
import { Hono } from 'hono'
import $, { db } from 'sdk.do'

const app = new Hono()

// Feature flags to control migration
const isFeatureMigrated = async (feature: string) => {
  const [flag] = await db.FeatureFlag.query({
    name: feature,
    enabled: true,
  })
  return !!flag
}

// Route to Business-as-Code or legacy
app.get('/orders/:id', async (c) => {
  const orderId = c.req.param('id')

  if (await isFeatureMigrated('orders')) {
    // New: Business-as-Code
    const order = await db.Order.get(orderId)
    return c.json(order)
  } else {
    // Legacy: Forward to old system
    const response = await fetch(`${process.env.LEGACY_API}/orders/${orderId}`)
    return c.json(await response.json())
  }
})

app.post('/orders', async (c) => {
  const data = await c.req.json()

  if (await isFeatureMigrated('order-creation')) {
    // New: Business-as-Code workflow
    const order = await $.Order.create(data)

    // Trigger workflow
    await send('$.Order.created', order)

    return c.json(order, 201)
  } else {
    // Legacy: Forward to old system
    const response = await fetch(`${process.env.LEGACY_API}/orders`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    })
    return c.json(await response.json(), response.status)
  }
})

Data Migration Pattern

// brownfield/data-migration.ts

export class DataMigrator {
  // Migrate data in batches
  async migrateEntity(sourceTable: string, targetType: string, batchSize: number = 1000) {
    let offset = 0
    let hasMore = true

    while (hasMore) {
      // Read from legacy database
      const batch = await this.readLegacyData(sourceTable, offset, batchSize)

      if (batch.length === 0) {
        hasMore = false
        break
      }

      // Transform to Business-as-Code format
      const transformed = batch.map((row) => this.transformRow(row, targetType))

      // Write to Business-as-Code database
      await db.batchCreate(targetType, transformed)

      logger.info(`Migrated batch`, {
        table: sourceTable,
        offset,
        count: batch.length,
      })

      offset += batchSize
    }
  }

  private async readLegacyData(table: string, offset: number, limit: number): Promise<any[]> {
    // Connect to legacy database
    const result = await legacyDb.query(`SELECT * FROM ${table} LIMIT ${limit} OFFSET ${offset}`)
    return result.rows
  }

  private transformRow(row: any, targetType: string): any {
    // Transform legacy schema to Schema.org
    switch (targetType) {
      case '$.Customer':
        return {
          $type: 'Customer',
          email: row.email,
          givenName: row.first_name,
          familyName: row.last_name,
          telephone: row.phone,
          // Map legacy fields to Schema.org properties
          dateCreated: row.created_at,
          identifier: row.legacy_id, // Preserve legacy ID for reference
        }

      case '$.Order':
        return {
          $type: 'Order',
          orderNumber: row.order_number,
          orderDate: row.created_at,
          orderStatus: this.mapOrderStatus(row.status),
          totalPrice: row.total,
          priceCurrency: row.currency || 'USD',
          identifier: row.legacy_id,
        }

      default:
        throw new Error(`Unknown target type: ${targetType}`)
    }
  }

  private mapOrderStatus(legacyStatus: string): string {
    const statusMap = {
      pending: 'OrderProcessing',
      paid: 'OrderPaymentDue',
      shipped: 'OrderInTransit',
      delivered: 'OrderDelivered',
      cancelled: 'OrderCancelled',
    }
    return statusMap[legacyStatus] || 'OrderProcessing'
  }

  // Dual-write pattern during migration
  async dualWrite(entity: string, data: any) {
    // Write to both systems
    const [legacyResult, bacResult] = await Promise.all([this.writeLegacy(entity, data), this.writeBusinessAsCode(entity, data)])

    // Verify consistency
    if (!this.areConsistent(legacyResult, bacResult)) {
      logger.error('Inconsistent write', {
        entity,
        legacy: legacyResult,
        bac: bacResult,
      })

      // Alert for manual review
      await alertManager.sendAlert({
        severity: AlertSeverity.ERROR,
        message: 'Dual write inconsistency detected',
        metadata: { entity, legacyResult, bacResult },
      })
    }

    return bacResult
  }

  private async writeLegacy(entity: string, data: any) {
    // Write to legacy system
    return await fetch(`${process.env.LEGACY_API}/${entity}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    })
  }

  private async writeBusinessAsCode(entity: string, data: any) {
    // Write to Business-as-Code
    return await db[entity].create(data)
  }

  private areConsistent(legacy: any, bac: any): boolean {
    // Compare key fields
    // Implement based on entity type
    return true
  }
}

// Usage
const migrator = new DataMigrator()

// Migrate customers
await migrator.migrateEntity('customers', '$.Customer')

// Migrate orders
await migrator.migrateEntity('orders', '$.Order')

Hybrid Approach

Run both systems in parallel:

Advantages

  • Lowest risk
  • Easy rollback
  • A/B testing possible
  • Gradual user migration

When to Use

  • Critical production systems
  • Regulatory requirements for gradual change
  • Large user bases
  • Uncertainty about new architecture

Implementation

// hybrid/traffic-splitter.ts

export class TrafficSplitter {
  private splitPercentage: number = 0 // Start with 0% to new system

  async routeRequest(request: Request): Promise<Response> {
    const shouldUseBusinessAsCode = this.shouldRoute(request)

    if (shouldUseBusinessAsCode) {
      return this.routeToBusinessAsCode(request)
    } else {
      return this.routeToLegacy(request)
    }
  }

  private shouldRoute(request: Request): boolean {
    // Route based on multiple factors

    // 1. Feature flag for specific user
    const userId = this.extractUserId(request)
    if (userId && this.isUserInBeta(userId)) {
      return true
    }

    // 2. Percentage-based routing
    const random = Math.random() * 100
    if (random < this.splitPercentage) {
      return true
    }

    // 3. Specific endpoints already migrated
    const path = new URL(request.url).pathname
    if (this.isMigratedEndpoint(path)) {
      return true
    }

    return false
  }

  private async routeToBusinessAsCode(request: Request): Promise<Response> {
    logger.info('Routing to Business-as-Code', {
      path: request.url,
      method: request.method,
    })

    try {
      // Process with Business-as-Code
      const response = await this.handleBusinessAsCode(request)

      // Log metrics
      await this.logMetrics('business-as-code', request, response)

      return response
    } catch (error) {
      logger.error('Business-as-Code error, falling back to legacy', error)

      // Fallback to legacy on error
      return this.routeToLegacy(request)
    }
  }

  private async routeToLegacy(request: Request): Promise<Response> {
    logger.info('Routing to legacy system', {
      path: request.url,
      method: request.method,
    })

    const response = await fetch(`${process.env.LEGACY_API}${new URL(request.url).pathname}`, {
      method: request.method,
      headers: request.headers,
      body: request.body,
    })

    await this.logMetrics('legacy', request, response)

    return response
  }

  // Gradually increase traffic to new system
  async increaseSplit(newPercentage: number) {
    if (newPercentage < 0 || newPercentage > 100) {
      throw new Error('Percentage must be between 0 and 100')
    }

    // Check error rates before increasing
    const errorRate = await this.getErrorRate('business-as-code')

    if (errorRate > 0.05) {
      throw new Error(`Error rate too high (${errorRate}), cannot increase traffic`)
    }

    this.splitPercentage = newPercentage

    logger.info('Updated traffic split', {
      newPercentage,
      errorRate,
    })

    // Store in database for persistence
    await db.upsert('$.TrafficSplit', {
      service: 'business-as-code',
      percentage: newPercentage,
      updatedAt: new Date(),
    })
  }

  private async getErrorRate(system: string): Promise<number> {
    const metrics = await db.query('$.Metric', {
      system,
      name: 'error_rate',
      'timestamp:gte': new Date(Date.now() - 3600000), // Last hour
    })

    if (metrics.length === 0) return 0

    const totalRequests = metrics.reduce((sum, m) => sum + m.requests, 0)
    const totalErrors = metrics.reduce((sum, m) => sum + m.errors, 0)

    return totalErrors / totalRequests
  }
}

// Gradual rollout schedule
const rollout = new TrafficSplitter()

// Week 1: 5%
await rollout.increaseSplit(5)

// Week 2: 10%
await rollout.increaseSplit(10)

// Week 3: 25%
await rollout.increaseSplit(25)

// Week 4: 50%
await rollout.increaseSplit(50)

// Week 5: 75%
await rollout.increaseSplit(75)

// Week 6: 100%
await rollout.increaseSplit(100)

Step-by-Step Migration

Extract Business Logic into Functions

Transform procedural code into semantic functions:

Before: Procedural Code

// legacy/order-processing.ts
export async function processOrder(orderId: string) {
  // Get order from database
  const order = await db.query('SELECT * FROM orders WHERE id = ?', [orderId])

  if (!order) {
    throw new Error('Order not found')
  }

  // Check inventory
  const items = await db.query('SELECT * FROM order_items WHERE order_id = ?', [orderId])

  for (const item of items) {
    const product = await db.query('SELECT * FROM products WHERE id = ?', [item.product_id])

    if (product.stock < item.quantity) {
      throw new Error(`Insufficient stock for product ${product.name}`)
    }
  }

  // Process payment
  const payment = await fetch('https://api.stripe.com/v1/charges', {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.STRIPE_KEY}` },
    body: JSON.stringify({
      amount: order.total * 100,
      currency: 'usd',
      source: order.payment_token,
    }),
  })

  if (!payment.ok) {
    throw new Error('Payment failed')
  }

  // Update inventory
  for (const item of items) {
    await db.query('UPDATE products SET stock = stock - ? WHERE id = ?', [item.quantity, item.product_id])
  }

  // Send confirmation email
  await fetch('https://api.sendgrid.com/v3/mail/send', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.SENDGRID_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      to: order.customer_email,
      from: '[email protected]',
      subject: 'Order Confirmation',
      html: `Your order ${order.order_number} has been confirmed!`,
    }),
  })

  // Update order status
  await db.query('UPDATE orders SET status = ? WHERE id = ?', ['processed', orderId])
}

After: Business-as-Code

// business-as-code/order-processing.ts
import $, { db, send, on } from 'sdk.do'

// Semantic workflow
on.Order.created(async (order: Order) => {
  // 1. Validate inventory (semantic operation)
  const validation = await send.Inventory.validate({
    items: order.orderedItem,
  })

  if (!validation.valid) {
    send.Order.cancel({
      order,
      reason: validation.reason,
    })
    return
  }

  // 2. Process payment (semantic operation)
  const payment = await send.Payment.process({
    order,
    amount: order.totalPrice,
  })

  if (payment.status !== 'succeeded') {
    send.Order.cancel({
      order,
      reason: 'Payment failed',
    })
    return
  }

  // 3. Reserve inventory (semantic operation)
  send.Inventory.reserve({
    items: order.orderedItem,
  })

  // 4. Send confirmation (semantic operation)
  send.Email.send({
    to: order.customer.email,
    template: 'order-confirmation',
    data: { order },
  })

  // 5. Update status (semantic operation)
  db.Order.update(order.$id, {
    orderStatus: 'OrderProcessing',
  })
})

// Individual semantic functions
$.Payment.process.implement(async ({ order, amount }) => {
  // Stripe integration abstracted
  const stripe = await $.Integration.get('stripe')

  return stripe.charge({
    amount,
    currency: order.priceCurrency,
    source: order.paymentMethod,
  })
})

$.Inventory.validate.implement(async ({ items }) => {
  for (const item of items) {
    const product = await db.Product.get(item.orderedItem.$id)

    if (product.quantityAvailable < item.orderQuantity) {
      return {
        valid: false,
        reason: `Insufficient inventory for ${product.name}`,
      }
    }
  }

  return { valid: true }
})

$.Inventory.reserve.implement(async ({ items }) => {
  for (const item of items) {
    db.Product.update(item.orderedItem.$id, {
      quantityAvailable: (product) => product.quantityAvailable - item.orderQuantity,
    })
  }
})

Convert Workflows to Declarative Definitions

Transform imperative workflows into declarative event-driven patterns:

// business-as-code/workflows/customer-onboarding.ts
import $, { on, send, db, ai } from 'sdk.do'

// Declarative workflow definition
export const customerOnboardingWorkflow = {
  name: 'Customer Onboarding',
  trigger: $.Customer.created,

  steps: [
    {
      name: 'send-welcome-email',
      action: $.Email.send,
      input: (customer) => ({
        to: customer.email,
        template: 'welcome',
        data: { customer },
      }),
    },
    {
      name: 'create-default-preferences',
      action: $.CustomerPreferences.create,
      input: (customer) => ({
        customer,
        notifications: true,
        newsletter: true,
      }),
    },
    {
      name: 'ai-personalization',
      action: ai.personalize,
      input: (customer) => ({
        customer,
        recommendations: true,
      }),
    },
    {
      name: 'assign-account-manager',
      action: $.Agent.assign,
      input: (customer) => ({
        agent: 'account-manager',
        customer,
      }),
      condition: (customer) => customer.tier === 'enterprise',
    },
  ],

  onError: {
    action: $.Task.create,
    input: (customer, error) => ({
      title: 'Onboarding Failed',
      description: `Customer ${customer.email} onboarding failed: ${error.message}`,
      assignedTo: 'support-team',
    }),
  },
}

// Register workflow
on(customerOnboardingWorkflow.trigger, async (customer) => {
  for (const step of customerOnboardingWorkflow.steps) {
    // Check condition
    if (step.condition && !step.condition(customer)) {
      continue
    }

    try {
      await send(step.action, step.input(customer))
    } catch (error) {
      await send(customerOnboardingWorkflow.onError.action, customerOnboardingWorkflow.onError.input(customer, error))
      throw error
    }
  }
})

Implement Event-Driven Patterns

Replace synchronous calls with asynchronous events:

sequenceDiagram participant Client participant API participant Queue participant Worker participant DB Client->>API: Create Order API->>DB: Save Order API->>Queue: Emit Order.created API-->>Client: 202 Accepted Queue->>Worker: Order.created event Worker->>DB: Process Order Worker->>Queue: Emit Payment.process Worker->>Queue: Emit Inventory.reserve Note over Worker: Asynchronous processing

Implementation:

// business-as-code/event-driven/order-api.ts
import { Hono } from 'hono'
import $, { db, send } from 'sdk.do'

const app = new Hono()

// Synchronous: Return immediately after creating order
app.post('/orders', async (c) => {
  const data = await c.req.json()

  // Create order
  const order = await $.Order.create({
    ...data,
    orderStatus: 'OrderProcessing',
    orderDate: new Date(),
  })

  // Emit event (async processing)
  await send('$.Order.created', order)

  // Return immediately
  return c.json(
    {
      order,
      message: 'Order created and will be processed asynchronously',
    },
    202 // Accepted
  )
})

// Client can poll or use webhooks for updates
app.get('/orders/:id', async (c) => {
  const order = await db.Order.get(c.req.param('id'))
  return c.json(order)
})

// Webhook for status updates
on.Order.statusChanged(async (order) => {
  if (order.webhookUrl) {
    fetch(order.webhookUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        event: 'order.status_changed',
        order,
      }),
    })
  }
})

Deploy Autonomous Agents

Replace manual processes with AI agents:

// business-as-code/agents/customer-support.ts
import $, { db, ai, send } from 'sdk.do'

export class CustomerSupportAgent {
  async handleTicket(ticket: Ticket) {
    // 1. Classify ticket
    const classification = await ai.classify({
      text: ticket.description,
      categories: ['technical', 'billing', 'product', 'urgent'],
    })

    // 2. Search knowledge base
    const knowledgeBase = await db.search('$.Article', {
      query: ticket.description,
      limit: 5,
    })

    // 3. Generate response
    const response = await ai.generate({
      model: 'gpt-5',
      prompt: 'Provide customer support response',
      context: {
        ticket,
        classification,
        knowledgeBase,
      },
    })

    // 4. Decide if human intervention needed
    if (response.confidence < 0.8 || classification.category === 'urgent') {
      // Escalate to human
      await send('$.Task.create', {
        title: `Customer Support: ${ticket.subject}`,
        description: ticket.description,
        assignedTo: 'support-team',
        priority: classification.category === 'urgent' ? 'high' : 'normal',
        metadata: {
          ticketId: ticket.$id,
          suggestedResponse: response.text,
        },
      })
    } else {
      // Send automated response
      await send('$.Email.send', {
        to: ticket.customer.email,
        subject: `Re: ${ticket.subject}`,
        body: response.text,
      })

      // Update ticket
      db.Ticket.update(ticket.$id, {
        status: 'resolved',
        resolution: response.text,
        resolvedBy: 'ai-agent',
        resolvedAt: new Date(),
      })
    }
  }
}

Migrate Data Models

Map legacy schemas to Schema.org types:

// migration/schema-mapper.ts

export class SchemaMapper {
  // Map legacy User to Schema.org Person
  legacyUserToPerson(legacyUser: LegacyUser): Person {
    return {
      $type: 'Person',
      givenName: legacyUser.first_name,
      familyName: legacyUser.last_name,
      email: legacyUser.email,
      telephone: legacyUser.phone,
      birthDate: legacyUser.date_of_birth,
      address: {
        $type: 'PostalAddress',
        streetAddress: legacyUser.address,
        addressLocality: legacyUser.city,
        addressRegion: legacyUser.state,
        postalCode: legacyUser.zip,
        addressCountry: legacyUser.country,
      },
      // Preserve legacy ID
      identifier: legacyUser.id,
    }
  }

  // Map legacy Product to Schema.org Product
  legacyProductToProduct(legacyProduct: LegacyProduct): Product {
    return {
      $type: 'Product',
      name: legacyProduct.name,
      description: legacyProduct.description,
      sku: legacyProduct.sku,
      brand: {
        $type: 'Brand',
        name: legacyProduct.brand_name,
      },
      offers: [
        {
          $type: 'Offer',
          price: legacyProduct.price,
          priceCurrency: legacyProduct.currency || 'USD',
          availability: this.mapAvailability(legacyProduct.in_stock),
        },
      ],
      image: legacyProduct.image_url,
      category: legacyProduct.category,
      quantityAvailable: legacyProduct.stock_quantity,
      identifier: legacyProduct.id,
    }
  }

  private mapAvailability(inStock: boolean): string {
    return inStock ? 'InStock' : 'OutOfStock'
  }
}

Integration Patterns

Legacy System Integration

Connect Business-as-Code with legacy systems:

// integration/legacy-adapter.ts
import $, { db } from 'sdk.do'

export class LegacyAdapter {
  // Read from legacy system
  async syncFromLegacy(entity: string) {
    const legacyData = await fetch(`${process.env.LEGACY_API}/${entity}`)
    const data = await legacyData.json()

    for (const item of data) {
      // Check if already exists
      const existing = await db[entity].query({
        identifier: item.id,
      })

      if (existing.length === 0) {
        // Create new
        await db[entity].create(this.transform(item, entity))
      } else {
        // Update existing
        await db[entity].update(existing[0].$id, this.transform(item, entity))
      }
    }
  }

  // Write to legacy system
  async syncToLegacy(entity: string, bacEntity: any) {
    const legacyFormat = this.transformToLegacy(bacEntity, entity)

    await fetch(`${process.env.LEGACY_API}/${entity}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(legacyFormat),
    })
  }

  private transform(legacyData: any, entity: string): any {
    // Transform legacy format to Business-as-Code format
    switch (entity) {
      case 'Customer':
        return {
          $type: 'Customer',
          email: legacyData.email,
          givenName: legacyData.first_name,
          familyName: legacyData.last_name,
          identifier: legacyData.id,
        }
      default:
        return legacyData
    }
  }

  private transformToLegacy(bacEntity: any, entity: string): any {
    // Transform Business-as-Code format to legacy format
    switch (entity) {
      case 'Customer':
        return {
          id: bacEntity.identifier,
          email: bacEntity.email,
          first_name: bacEntity.givenName,
          last_name: bacEntity.familyName,
        }
      default:
        return bacEntity
    }
  }
}

API Bridging

Create adapter layer:

// integration/api-bridge.ts
import { Hono } from 'hono'
import $, { db } from 'sdk.do'

const app = new Hono()

// Legacy API format -> Business-as-Code
app.post('/legacy/users', async (c) => {
  const legacyUser = await c.req.json()

  // Transform to Business-as-Code Person
  const person = await $.Person.create({
    givenName: legacyUser.first_name,
    familyName: legacyUser.last_name,
    email: legacyUser.email,
    identifier: legacyUser.user_id,
  })

  // Return in legacy format
  return c.json({
    user_id: person.identifier,
    first_name: person.givenName,
    last_name: person.familyName,
    email: person.email,
  })
})

// Business-as-Code -> Legacy API format
app.get('/legacy/users/:id', async (c) => {
  const [person] = await db.Person.query({
    identifier: c.req.param('id'),
  })

  if (!person) {
    return c.json({ error: 'User not found' }, 404)
  }

  // Return in legacy format
  return c.json({
    user_id: person.identifier,
    first_name: person.givenName,
    last_name: person.familyName,
    email: person.email,
  })
})

Data Synchronization

Keep systems in sync during migration:

// integration/data-sync.ts
import $, { on, send, db } from 'sdk.do'

export class DataSync {
  // Bi-directional sync
  async initialize() {
    // Business-as-Code -> Legacy
    on.Customer.created(this.syncCustomerToLegacy.bind(this))
    on.Customer.updated(this.syncCustomerToLegacy.bind(this))

    // Legacy -> Business-as-Code (via webhook or polling)
    this.pollLegacyChanges()
  }

  private async syncCustomerToLegacy(customer: Customer) {
    try {
      fetch(`${process.env.LEGACY_API}/customers`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          id: customer.identifier,
          email: customer.email,
          first_name: customer.givenName,
          last_name: customer.familyName,
        }),
      })
    } catch (error) {
      logger.error('Failed to sync customer to legacy', error)

      // Queue for retry
      db.SyncQueue.create({
        $type: 'SyncQueue',
        entity: 'Customer',
        entityId: customer.$id,
        direction: 'to_legacy',
        retryCount: 0,
      })
    }
  }

  private async pollLegacyChanges() {
    setInterval(async () => {
      try {
        const response = await fetch(`${process.env.LEGACY_API}/customers/changes`)
        const changes = await response.json()

        for (const change of changes) {
          await this.applyChange(change)
        }
      } catch (error) {
        logger.error('Failed to poll legacy changes', error)
      }
    }, 60000) // Poll every minute
  }

  private async applyChange(change: any) {
    switch (change.type) {
      case 'customer_created':
      case 'customer_updated':
        const [existing] = await db.Customer.query({
          identifier: change.data.id,
        })

        if (existing) {
          db.Customer.update(existing.$id, {
            email: change.data.email,
            givenName: change.data.first_name,
            familyName: change.data.last_name,
          })
        } else {
          await $.Customer.create({
            identifier: change.data.id,
            email: change.data.email,
            givenName: change.data.first_name,
            familyName: change.data.last_name,
          })
        }
        break
    }
  }
}

Event Streaming

Stream events between systems:

// integration/event-stream.ts
import $, { send } from 'sdk.do'

export class EventStreamBridge {
  // Kafka/RabbitMQ -> Business-as-Code
  async consumeExternalEvents() {
    const consumer = kafka.consumer({ groupId: 'business-as-code' })

    await consumer.connect()
    await consumer.subscribe({ topic: 'legacy-events' })

    await consumer.run({
      eachMessage: async ({ message }) => {
        const event = JSON.parse(message.value.toString())

        // Transform and emit as Business-as-Code event
        await this.transformAndEmit(event)
      },
    })
  }

  // Business-as-Code -> Kafka/RabbitMQ
  async produceBusinessEvents() {
    on.Order.created(async (order) => {
      kafka.producer().send({
        topic: 'business-events',
        messages: [
          {
            key: order.$id,
            value: JSON.stringify({
              type: 'order.created',
              data: order,
              timestamp: Date.now(),
            }),
          },
        ],
      })
    })
  }

  private async transformAndEmit(externalEvent: any) {
    switch (externalEvent.type) {
      case 'user.created':
        await send('$.Customer.created', {
          $type: 'Customer',
          email: externalEvent.data.email,
          givenName: externalEvent.data.firstName,
          familyName: externalEvent.data.lastName,
        })
        break

      case 'order.placed':
        await send('$.Order.created', {
          $type: 'Order',
          orderNumber: externalEvent.data.orderNumber,
          totalPrice: externalEvent.data.total,
          orderStatus: 'OrderProcessing',
        })
        break
    }
  }
}

Webhook Patterns

Legacy systems notify Business-as-Code via webhooks:

// integration/webhook-receiver.ts
import { Hono } from 'hono'
import $, { send } from 'sdk.do'

const app = new Hono()

// Receive webhooks from legacy system
app.post('/webhooks/legacy/:event', async (c) => {
  const event = c.req.param('event')
  const data = await c.req.json()

  // Verify webhook signature
  if (!this.verifySignature(c.req.header('x-signature'), data)) {
    return c.json({ error: 'Invalid signature' }, 401)
  }

  // Transform to Business-as-Code event
  await this.handleLegacyEvent(event, data)

  return c.json({ received: true })
})

private async handleLegacyEvent(event: string, data: any) {
  switch (event) {
    case 'payment_succeeded':
      await send('$.Payment.completed', {
        $type: 'Payment',
        amount: data.amount / 100,
        priceCurrency: data.currency,
        paymentStatus: 'PaymentComplete',
      })
      break

    case 'user_registered':
      await send('$.Customer.created', {
        $type: 'Customer',
        email: data.email,
        givenName: data.first_name,
        familyName: data.last_name,
      })
      break
  }
}

private verifySignature(signature: string, data: any): boolean {
  const expectedSignature = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET!)
    .update(JSON.stringify(data))
    .digest('hex')

  return signature === expectedSignature
}

Testing During Migration

Parallel Running

Run both systems and compare results:

// testing/parallel-runner.ts

export class ParallelRunner {
  async compareImplementations(request: Request): Promise<ComparisonResult> {
    // Run both systems
    const [legacyResult, bacResult] = await Promise.all([this.runLegacy(request), this.runBusinessAsCode(request)])

    // Compare results
    const comparison = {
      match: this.resultsMatch(legacyResult, bacResult),
      legacy: legacyResult,
      businessAsCode: bacResult,
      differences: this.findDifferences(legacyResult, bacResult),
    }

    // Log comparison
    await this.logComparison(comparison)

    // Alert on mismatch
    if (!comparison.match) {
      await alertManager.sendAlert({
        severity: AlertSeverity.WARNING,
        message: 'Implementation mismatch detected',
        metadata: comparison,
      })
    }

    // Return Business-as-Code result (or legacy if safer)
    return comparison.match ? bacResult : legacyResult
  }

  private async runLegacy(request: Request) {
    const start = Date.now()

    try {
      const response = await fetch(`${process.env.LEGACY_API}${request.url}`, {
        method: request.method,
        headers: request.headers,
        body: request.body,
      })

      return {
        success: true,
        data: await response.json(),
        duration: Date.now() - start,
      }
    } catch (error) {
      return {
        success: false,
        error: error.message,
        duration: Date.now() - start,
      }
    }
  }

  private async runBusinessAsCode(request: Request) {
    const start = Date.now()

    try {
      const result = await this.handleBusinessAsCodeRequest(request)

      return {
        success: true,
        data: result,
        duration: Date.now() - start,
      }
    } catch (error) {
      return {
        success: false,
        error: error.message,
        duration: Date.now() - start,
      }
    }
  }

  private resultsMatch(legacy: any, bac: any): boolean {
    if (legacy.success !== bac.success) return false

    // Deep comparison of data
    return JSON.stringify(legacy.data) === JSON.stringify(bac.data)
  }

  private findDifferences(legacy: any, bac: any): Difference[] {
    const differences: Difference[] = []

    // Compare each field
    const legacyKeys = Object.keys(legacy.data || {})
    const bacKeys = Object.keys(bac.data || {})

    for (const key of new Set([...legacyKeys, ...bacKeys])) {
      if (legacy.data[key] !== bac.data[key]) {
        differences.push({
          field: key,
          legacy: legacy.data[key],
          businessAsCode: bac.data[key],
        })
      }
    }

    return differences
  }
}

Shadow Testing

Test new system without affecting users:

// testing/shadow-testing.ts

export class ShadowTester {
  async shadowTest(request: Request): Promise<Response> {
    // Process request with legacy system (real)
    const legacyResponse = await this.processLegacy(request)

    // Process same request with Business-as-Code (shadow)
    // Run async, don't wait
    this.processShadow(request).catch((error) => {
      logger.error('Shadow test error', error)
    })

    // Return legacy response
    return legacyResponse
  }

  private async processShadow(request: Request) {
    try {
      const bacResponse = await this.processBusinessAsCode(request)

      // Compare with legacy (would need to store legacy result)
      await this.compareAndLog(request, bacResponse)
    } catch (error) {
      // Log shadow test failures
      db.ShadowTestFailure.create({
        $type: 'ShadowTestFailure',
        request: {
          url: request.url,
          method: request.method,
        },
        error: error.message,
        timestamp: new Date(),
      })
    }
  }
}

A/B Testing

Compare systems with real users:

// testing/ab-testing.ts

export class ABTester {
  async test(request: Request): Promise<Response> {
    const userId = this.extractUserId(request)
    const variant = this.assignVariant(userId)

    if (variant === 'a') {
      // Legacy system
      return this.processLegacy(request)
    } else {
      // Business-as-Code system
      return this.processBusinessAsCode(request)
    }
  }

  private assignVariant(userId: string): 'a' | 'b' {
    // Consistent assignment based on user ID
    const hash = crypto.createHash('md5').update(userId).digest('hex')
    const value = parseInt(hash.substring(0, 8), 16)

    return value % 2 === 0 ? 'a' : 'b'
  }

  // Track metrics by variant
  async trackMetric(userId: string, metric: string, value: number) {
    const variant = this.assignVariant(userId)

    db.ABTestMetric.create({
      $type: 'ABTestMetric',
      variant,
      metric,
      value,
      timestamp: new Date(),
    })
  }

  // Analyze results
  async analyzeResults() {
    const metrics = await db.ABTestMetric.query({})

    const byVariant = {
      a: metrics.filter((m) => m.variant === 'a'),
      b: metrics.filter((m) => m.variant === 'b'),
    }

    return {
      variantA: this.calculateStats(byVariant.a),
      variantB: this.calculateStats(byVariant.b),
      significant: this.isStatisticallySignificant(byVariant.a, byVariant.b),
    }
  }

  private calculateStats(metrics: any[]) {
    const values = metrics.map((m) => m.value)

    return {
      count: values.length,
      mean: values.reduce((a, b) => a + b, 0) / values.length,
      min: Math.min(...values),
      max: Math.max(...values),
    }
  }

  private isStatisticallySignificant(a: any[], b: any[]): boolean {
    // T-test or other statistical test
    // Simplified for example
    return Math.abs(this.calculateStats(a).mean - this.calculateStats(b).mean) > 0.1
  }
}

Rollback Strategies

Quick rollback if issues detected:

// testing/rollback.ts

export class MigrationRollback {
  async checkHealthAndRollback() {
    const health = await this.checkSystemHealth()

    if (!health.healthy) {
      logger.error('System unhealthy, initiating rollback', health)
      await this.rollback()
    }
  }

  private async checkSystemHealth() {
    const checks = await Promise.all([this.checkErrorRate(), this.checkResponseTime(), this.checkDataIntegrity()])

    return {
      healthy: checks.every((c) => c.passed),
      checks,
    }
  }

  private async checkErrorRate(): Promise<HealthCheck> {
    const errorRate = await this.getErrorRate()

    return {
      name: 'error_rate',
      passed: errorRate < 0.05,
      value: errorRate,
      threshold: 0.05,
    }
  }

  private async checkResponseTime(): Promise<HealthCheck> {
    const p95 = await this.getP95ResponseTime()

    return {
      name: 'response_time_p95',
      passed: p95 < 1000,
      value: p95,
      threshold: 1000,
    }
  }

  private async checkDataIntegrity(): Promise<HealthCheck> {
    // Compare row counts, checksums, etc.
    const legacyCount = await this.getLegacyCount()
    const bacCount = await this.getBusinessAsCodeCount()

    const difference = Math.abs(legacyCount - bacCount) / legacyCount

    return {
      name: 'data_integrity',
      passed: difference < 0.01, // Less than 1% difference
      value: difference,
      threshold: 0.01,
    }
  }

  private async rollback() {
    // Switch traffic back to legacy
    await this.switchTrafficToLegacy()

    // Alert team
    await alertManager.sendAlert({
      severity: AlertSeverity.CRITICAL,
      message: 'Migration rolled back due to health check failures',
    })

    // Create incident
    db.Incident.create({
      $type: 'Incident',
      title: 'Migration Rollback',
      severity: 'critical',
      timestamp: new Date(),
    })
  }
}

Case Studies

Monolith to Business-as-Code

Company: E-commerce Platform Before: Rails monolith, 500K LOC After: Business-as-Code autonomous business Timeline: 12 months Team: 15 developers

Challenges

  • 10-year-old codebase with significant technical debt
  • 5M active users requiring zero downtime
  • Complex checkout workflow with 20+ steps
  • Integration with 30+ third-party services

Approach

  1. Assessment (Month 1-2)

    • Mapped 200 API endpoints
    • Identified 15 core business workflows
    • Extracted 50 business entities
  2. Foundation (Month 3-4)

    • Set up Business-as-Code infrastructure
    • Mapped data models to Schema.org
    • Created integration adapters
  3. Incremental Migration (Month 5-10)

    • Migrated one workflow per sprint
    • Started with new features (greenfield)
    • Moved to read-only endpoints
    • Finally migrated write operations
  4. Cutover (Month 11-12)

    • Ran parallel systems for 2 months
    • Gradually shifted traffic
    • Decommissioned monolith

Results

  • Performance: 60% faster response times
  • Development Speed: 3x faster feature development
  • Costs: 40% reduction in infrastructure costs
  • Quality: 80% reduction in bugs
  • Team Satisfaction: Significantly improved

Microservices to Business-as-Code

Company: SaaS Platform Before: 40 microservices, complex orchestration After: Unified Business-as-Code system Timeline: 9 months Team: 20 developers

Challenges

  • Service sprawl with unclear boundaries
  • Complex inter-service communication
  • Duplicate business logic across services
  • Difficult to maintain consistency

Approach

  1. Consolidation (Month 1-3)

    • Grouped services by business domain
    • Identified shared business logic
    • Created semantic interface layer
  2. Semantic Layer (Month 4-6)

    • Defined semantic operations ($.Subject.predicate.Object)
    • Implemented semantic routing
    • Migrated services to semantic patterns
  3. Simplification (Month 7-9)

    • Replaced complex orchestration with events
    • Unified data models with Schema.org
    • Deployed autonomous agents

Results

  • Services: Reduced from 40 to 5 semantic domains
  • Complexity: 70% reduction in inter-service calls
  • Consistency: Unified business logic
  • Observability: Single semantic trace
  • Onboarding: New developers productive in days vs weeks

Serverless to Business-as-Code

Company: FinTech Startup Before: 100+ AWS Lambda functions After: Business-as-Code autonomous finance platform Timeline: 6 months Team: 8 developers

Challenges

  • Function sprawl and naming inconsistency
  • Cold start latency issues
  • Complex IAM and permission management
  • Difficult local development and testing

Approach

  1. Function Mapping (Month 1-2)

    • Mapped functions to semantic operations
    • Identified shared patterns
    • Grouped by business capability
  2. Semantic Functions (Month 3-4)

    • Implemented $.Subject.predicate.Object pattern
    • Created semantic function registry
    • Added intelligent routing
  3. Platform Migration (Month 5-6)

    • Migrated to Business-as-Code runtime
    • Implemented edge deployment
    • Added AI-driven optimization

Results

  • Functions: Reduced from 100+ to 20 semantic operations
  • Latency: Zero cold starts
  • Development: Unified local dev experience
  • Testing: Comprehensive test coverage
  • Costs: 50% reduction in compute costs

Summary

Migration to Business-as-Code is a journey that requires:

  1. Thorough Assessment: Understand your current architecture and identify migration candidates

  2. Right Strategy: Choose greenfield, brownfield, or hybrid based on your situation

  3. Step-by-Step Approach: Extract business logic, convert workflows, implement events, deploy agents

  4. Integration Patterns: Bridge legacy systems with adapters, synchronization, and event streaming

  5. Continuous Testing: Use parallel running, shadow testing, and A/B testing to ensure reliability

  6. Quick Rollback: Have rollback procedures ready for when issues arise

Next Steps

  1. Start Small: Begin with a single feature or workflow
  2. Measure Everything: Track metrics throughout migration
  3. Iterate: Learn from each step and adjust approach
  4. Communicate: Keep team and stakeholders informed
  5. Celebrate: Recognize milestones and successes

For production best practices, see Best Practices Guide