Skip to main content

Operational tuning

Operational tuning lets you adjust system parameters without deploying. Use it for:

  • Rate limits and quotas
  • Timeouts and retries
  • Batch sizes and intervals
  • Cache TTLs
  • Thresholds and limits

Why use dynamic configuration?

Traditional approach:

// Hardcoded - requires deploy to change
const RATE_LIMIT = 100;
const TIMEOUT_MS = 5000;
const BATCH_SIZE = 50;

With Replane:

// Dynamic - change in dashboard, updates instantly
const rateLimit = replane.get('api-rate-limit');
const timeout = replane.get('request-timeout-ms');
const batchSize = replane.get('batch-size');

Common use cases

Rate limiting

Adjust API rate limits per plan:

import { Replane } from '@replanejs/sdk'

interface Configs {
'rate-limit-requests-per-minute': number;
}

const replane = new Replane<Configs>()
await replane.connect({
sdkKey: process.env.REPLANE_SDK_KEY!,
baseUrl: 'https://replane.example.com'
})

function checkRateLimit(user: User) {
const limit = replane.get('rate-limit-requests-per-minute', {
context: { plan: user.plan }
});

return user.requestCount < limit;
}

Config setup:

Name: rate-limit-requests-per-minute
Base value: 60

Override: Premium users
Condition: plan equals "premium"
Value: 1000

Override: Enterprise users
Condition: plan equals "enterprise"
Value: 10000

Timeouts

Adjust timeouts based on conditions:

interface Configs {
'api-timeout-ms': number;
'retry-count': number;
'retry-delay-ms': number;
}

async function fetchWithTimeout(url: string) {
const timeout = replane.get('api-timeout-ms');
const retries = replane.get('retry-count');
const retryDelay = replane.get('retry-delay-ms');

for (let i = 0; i <= retries; i++) {
try {
const response = await fetch(url, {
signal: AbortSignal.timeout(timeout)
});
return response;
} catch (error) {
if (i < retries) {
await sleep(retryDelay);
}
}
}
throw new Error('Request failed after retries');
}

Batch processing

Tune batch job parameters:

interface Configs {
'batch-size': number;
'batch-interval-ms': number;
'concurrent-workers': number;
}

async function processBatch() {
const batchSize = replane.get('batch-size');
const interval = replane.get('batch-interval-ms');
const workers = replane.get('concurrent-workers');

const items = await getNextItems(batchSize);

await Promise.all(
chunk(items, Math.ceil(items.length / workers))
.map(chunk => processItems(chunk))
);

setTimeout(processBatch, interval);
}

Cache configuration

Dynamic cache TTLs:

interface Configs {
'cache-ttl-seconds': number;
'cache-max-items': number;
}

class ConfigurableCache {
async get(key: string) {
const ttl = replane.get('cache-ttl-seconds');
const maxItems = replane.get('cache-max-items');

// Use values for cache behavior
}
}

Feature limits

Control feature quotas:

interface Configs {
'max-file-size-mb': number;
'max-uploads-per-day': number;
'max-team-members': number;
}

function validateUpload(file: File, user: User) {
const maxSizeMb = replane.get('max-file-size-mb', {
context: { plan: user.plan }
});

if (file.size > maxSizeMb * 1024 * 1024) {
throw new Error(`File too large. Max: ${maxSizeMb}MB`);
}
}

Structured configuration

For complex settings, use objects:

interface Configs {
'email-settings': {
enabled: boolean;
rateLimit: number;
retries: number;
providers: string[];
};
}

const emailSettings = replane.get('email-settings');

if (emailSettings.enabled) {
await sendEmail({
maxRetries: emailSettings.retries,
rateLimit: emailSettings.rateLimit
});
}

JSON Schema validation

Prevent invalid values with JSON Schema:

{
"type": "object",
"properties": {
"enabled": { "type": "boolean" },
"rateLimit": { "type": "number", "minimum": 1, "maximum": 10000 },
"retries": { "type": "integer", "minimum": 0, "maximum": 10 }
},
"required": ["enabled", "rateLimit"]
}

The dashboard validates changes against the schema before saving.

Reacting to changes

Update behavior when configs change:

// Initial setup
let rateLimit = replane.get('api-rate-limit');

// Update when config changes
replane.subscribe('api-rate-limit', (config) => {
rateLimit = config.value;
console.log('Rate limit updated:', rateLimit);
});

// Use the current value
function checkRateLimit() {
return requestCount < rateLimit;
}

Environment-specific values

Different values per environment:

ConfigProductionStagingDevelopment
api-rate-limit100100010000
request-timeout-ms50003000060000
log-level"error""info""debug"

Best practices

Document your configs

Add descriptions in the dashboard explaining:

  • What the config controls
  • Valid value ranges
  • Impact of changes

Monitor after changes

After changing operational configs:

  1. Watch metrics (latency, error rates)
  2. Be ready to roll back
  3. Consider gradual changes (100 → 150 → 200)

Separate concerns

Keep configs focused:

✓ api-rate-limit
✓ api-timeout-ms
✓ api-retry-count

✗ api-settings (too broad)

Incident response

Use dynamic configuration for quick incident response:

// Kill switch for expensive operation
if (!replane.get('feature-search-enabled')) {
return { error: 'Search is temporarily disabled' };
}

// Circuit breaker
const circuitOpen = replane.get('circuit-breaker-open');
if (circuitOpen) {
return getCachedResponse();
}

// Reduce load
const degradedMode = replane.get('degraded-mode-enabled');
if (degradedMode) {
return getSimplifiedResponse();
}

Next steps