Production Deployment

Deploy your dcupl application to production with this comprehensive checklist and best practices guide. Covers performance optimization, security, monitoring, and common deployment scenarios.

Pre-Deployment Checklist

Code Quality

  • All quality validation errors resolved
  • TypeScript compilation passes without errors
  • Tests passing (unit, integration)
  • No console.log statements in production code
  • Error handling implemented for all async operations

Configuration

  • Production environment variables configured
  • API keys rotated and secured
  • Published version of Console configuration
  • Quality validation disabled for performance
  • CDN URLs verified

Performance

  • Bundle size optimized (tree shaking working)
  • Data loading strategy defined
  • Caching strategy implemented
  • Large dataset handling tested

Security

  • Secrets not committed to repository
  • API keys scoped to production project
  • CORS configured correctly
  • Content Security Policy set

Production Configuration

Optimized dcupl Setup

src/config/production.ts
import { Dcupl } from '@dcupl/core';
import { DcuplAppLoader } from '@dcupl/loader';

export function createProductionDcupl() {
  const dcupl = new Dcupl({
    config: {
      projectId: process.env.DCUPL_PROJECT_ID!,
      apiKey: process.env.DCUPL_API_KEY!,
    },
    quality: {
      enabled: false, // Disable for performance
    },
  });

  const loader = new DcuplAppLoader();
  dcupl.loaders.add(loader);

  return { dcupl, loader };
}

export async function initializeProduction() {
  const { dcupl, loader } = createProductionDcupl();

  try {
    // Fetch published configuration
    await loader.config.fetch();
    await loader.process({ applicationKey: 'default' });
    await dcupl.init();

    return dcupl;
  } catch (error) {
    console.error('Failed to initialize dcupl:', error);
    throw error;
  }
}

Environment Variables

.env.production
# Production Configuration
DCUPL_PROJECT_ID=prod-project-id
DCUPL_API_KEY=prod-api-key-xxx
NODE_ENV=production

Performance Optimization

1. Disable Quality Validation

Quality validation adds overhead. Disable in production:

const dcupl = new Dcupl({
  quality: {
    enabled: false, // Production: disabled
  },
});

Impact: 10-30% faster initialization

2. Use Partial Updates

Avoid full data reloads when possible:

// Bad: Full reload
dcupl.data.set(allData, { model: 'Product' });
await dcupl.init();

// Good: Partial update (10-100x faster)
dcupl.data.update({
  model: 'Product',
  data: [{ key: 'p1', price: 999 }],
});

3. Implement List Pooling

Reuse lists instead of creating new ones:

class ListPool {
  private pool: Map<string, DcuplList[]> = new Map();

  get(dcupl: Dcupl, modelKey: string): DcuplList {
    const pool = this.pool.get(modelKey) || [];
    if (pool.length > 0) {
      return pool.pop()!;
    }
    return dcupl.lists.create({ modelKey });
  }

  release(list: DcuplList) {
    list.catalog.query.clear();
    const modelKey = list.model.key;
    const pool = this.pool.get(modelKey) || [];
    pool.push(list);
    this.pool.set(modelKey, pool);
  }
}

4. Use Pagination

For large result sets:

const productList = dcupl.lists.create({
  modelKey: 'Product',
  pagination: {
    page: 1,
    pageSize: 50, // Reasonable page size
  },
});

5. Lazy Load Non-Critical Data

Load essential data first, supplementary data later:

async function initializeApp(dcupl: Dcupl) {
  // 1. Load critical data
  await loadCriticalData(dcupl);

  // 2. Render initial UI
  renderApp();

  // 3. Load supplementary data in background
  loadSupplementaryData(dcupl);
}

Error Handling

Graceful Initialization

export async function initWithFallback() {
  const { dcupl, loader } = createProductionDcupl();

  try {
    await loader.config.fetch();
    await loader.process({ applicationKey: 'default' });
    await dcupl.init();
    return { dcupl, status: 'ready' };
  } catch (error) {
    console.error('dcupl initialization failed:', error);

    // Option 1: Return degraded state
    return { dcupl: null, status: 'error', error };

    // Option 2: Retry with exponential backoff
    // return retryInit(dcupl, loader);
  }
}

Retry Logic

async function retryInit(
  dcupl: Dcupl,
  loader: DcuplAppLoader,
  maxRetries = 3
): Promise<Dcupl> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      await loader.config.fetch();
      await loader.process({ applicationKey: 'default' });
      await dcupl.init();
      return dcupl;
    } catch (error) {
      console.error(`Init attempt ${attempt} failed:`, error);

      if (attempt === maxRetries) {
        throw new Error(`Failed after ${maxRetries} attempts`);
      }

      // Exponential backoff
      await new Promise(resolve =>
        setTimeout(resolve, 1000 * Math.pow(2, attempt - 1))
      );
    }
  }
  throw new Error('Unreachable');
}

Query Error Handling

function safeQuery(list: DcuplList) {
  try {
    return list.catalog.query.items();
  } catch (error) {
    console.error('Query failed:', error);
    return []; // Return empty array as fallback
  }
}

Monitoring

Performance Metrics

async function initWithMetrics(dcupl: Dcupl, loader: DcuplAppLoader) {
  const startTime = performance.now();

  await loader.config.fetch();
  const configTime = performance.now();

  await loader.process({ applicationKey: 'default' });
  const processTime = performance.now();

  await dcupl.init();
  const initTime = performance.now();

  // Log metrics
  console.log({
    configFetch: configTime - startTime,
    process: processTime - configTime,
    init: initTime - processTime,
    total: initTime - startTime,
  });

  // Send to analytics
  sendMetrics({
    dcuplConfigFetch: configTime - startTime,
    dcuplProcess: processTime - configTime,
    dcuplInit: initTime - processTime,
  });
}

Quality Analytics (Development Only)

if (process.env.NODE_ENV !== 'production') {
  await dcupl.init();
  const analytics = dcupl.quality.values;

  console.log({
    loading: analytics.loading,
    processing: analytics.processing,
    startup: analytics.startup,
    counts: analytics.counts,
    errors: dcupl.quality.getErrors(),
  });
}

Health Check Endpoint

// Express/Node.js example
app.get('/health/dcupl', (req, res) => {
  try {
    // Check if dcupl is initialized
    const list = dcupl.lists.create({ modelKey: 'Product' });
    const count = list.catalog.query.count();
    list.destroy();

    res.json({
      status: 'healthy',
      productCount: count,
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    res.status(503).json({
      status: 'unhealthy',
      error: error.message,
      timestamp: new Date().toISOString(),
    });
  }
});

Deployment Strategies

Static Site (Vercel, Netlify)

src/lib/dcupl.ts
let dcuplInstance: Dcupl | null = null;

export async function getDcupl(): Promise<Dcupl> {
  if (dcuplInstance) {
    return dcuplInstance;
  }

  const dcupl = new Dcupl({
    config: {
      projectId: process.env.NEXT_PUBLIC_DCUPL_PROJECT_ID!,
      apiKey: process.env.NEXT_PUBLIC_DCUPL_API_KEY!,
    },
    quality: { enabled: false },
  });

  const loader = new DcuplAppLoader();
  dcupl.loaders.add(loader);

  await loader.config.fetch();
  await loader.process({ applicationKey: 'default' });
  await dcupl.init();

  dcuplInstance = dcupl;
  return dcupl;
}

Docker Container

Dockerfile
FROM node:20-alpine

WORKDIR /app

# Copy package files
COPY package*.json ./
RUN npm ci --only=production

# Copy app files
COPY . .

# Build
RUN npm run build

# Set environment
ENV NODE_ENV=production

# Run
CMD ["npm", "start"]
docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=production
      - DCUPL_PROJECT_ID=${DCUPL_PROJECT_ID}
      - DCUPL_API_KEY=${DCUPL_API_KEY}

Kubernetes

k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dcupl-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: dcupl-app
  template:
    metadata:
      labels:
        app: dcupl-app
    spec:
      containers:
        - name: app
          image: your-registry/dcupl-app:latest
          ports:
            - containerPort: 3000
          env:
            - name: NODE_ENV
              value: 'production'
            - name: DCUPL_PROJECT_ID
              valueFrom:
                secretKeyRef:
                  name: dcupl-secrets
                  key: project-id
            - name: DCUPL_API_KEY
              valueFrom:
                secretKeyRef:
                  name: dcupl-secrets
                  key: api-key
          resources:
            requests:
              memory: '256Mi'
              cpu: '100m'
            limits:
              memory: '512Mi'
              cpu: '500m'
          livenessProbe:
            httpGet:
              path: /health/dcupl
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 10

Security Considerations

API Key Management

// Never hardcode API keys
const dcupl = new Dcupl({
  config: {
    projectId: process.env.DCUPL_PROJECT_ID!, // From environment
    apiKey: process.env.DCUPL_API_KEY!, // From environment
  },
});

Secrets Storage

.github/workflows/deploy.yml
# GitHub Actions example
- name: Deploy
  env:
    DCUPL_PROJECT_ID: ${{ secrets.DCUPL_PROJECT_ID }}
    DCUPL_API_KEY: ${{ secrets.DCUPL_API_KEY }}
  run: npm run deploy

Content Security Policy

// Next.js example
const cspHeader = `
  default-src 'self';
  script-src 'self' 'unsafe-eval' 'unsafe-inline';
  connect-src 'self' https://cdn.dcupl.com https://api.dcupl.com;
  img-src 'self' blob: data:;
`;

Post-Deployment Checklist

Immediate Checks

  • Application loads without errors
  • dcupl initialization succeeds
  • Queries return expected results
  • No console errors in browser
  • Health check endpoint responds

Ongoing Monitoring

  • Error tracking configured (Sentry, etc.)
  • Performance monitoring active
  • Alerting for initialization failures
  • Log aggregation configured

Rollback Plan

  • Previous version tagged and deployable
  • Rollback procedure documented
  • Team aware of rollback process

Troubleshooting Production Issues

Slow Initialization

Symptoms: Long loading time before app is usable

Solutions:

  1. Check network latency to CDN
  2. Verify configuration file size
  3. Implement loading indicator
  4. Consider progressive loading

Memory Issues

Symptoms: Out of memory errors, slow performance

Solutions:

  1. Reduce dataset size
  2. Implement pagination
  3. Destroy unused lists
  4. Monitor with dcupl.quality.values

Stale Data

Symptoms: Old data showing after publish

Solutions:

  1. Verify published version is selected
  2. Check CDN cache invalidation
  3. Implement cache-busting
  4. Force reload configuration

What's Next?