Best Practices
Proven patterns and guidelines for building successful services
Proven patterns and guidelines for building successful, scalable, and profitable services.
Service Design
1. Single Responsibility Principle
Each service should do one thing well:
// Good: Focused service
const emailValidationService = await $.Service.create({
name: 'Email Validator',
purpose: 'Validate email addresses',
does: ['syntax-check', 'domain-validation', 'smtp-verification'],
doesNot: ['email-marketing', 'email-sending', 'contact-management'],
})
// Avoid: Kitchen sink service
const everythingService = await $.Service.create({
name: 'Everything Service',
does: [
'email-validation',
'crm-sync',
'content-generation',
'payment-processing',
// ... too many responsibilities
],
})Why:
- Easier to test and maintain
- Simpler pricing models
- Better composability
- Clearer value proposition
2. Clear Input/Output Contracts
Define explicit schemas:
const service = await $.Service.create({
name: 'Document Summarizer',
// Explicit input schema
input: {
schema: {
document: {
type: 'string',
required: true,
minLength: 100,
maxLength: 100000,
description: 'The document text to summarize',
},
maxLength: {
type: 'number',
required: false,
default: 500,
min: 100,
max: 2000,
description: 'Maximum length of summary in words',
},
style: {
type: 'enum',
required: false,
values: ['concise', 'detailed', 'bullet-points'],
default: 'concise',
description: 'Summary style',
},
},
},
// Explicit output schema
output: {
schema: {
summary: {
type: 'string',
description: 'The generated summary',
},
keyPoints: {
type: 'array',
items: 'string',
description: 'Key points extracted from document',
},
wordCount: {
type: 'number',
description: 'Word count of summary',
},
confidence: {
type: 'number',
min: 0,
max: 1,
description: 'Confidence score of summarization',
},
},
},
})3. Graceful Degradation
Handle failures elegantly:
on.ServiceRequest.created(async (request) => {
try {
// Try primary method
const result = await primaryMethod(request)
return result
} catch (primaryError) {
try {
// Fallback to secondary method
logger.warn('Primary method failed, using fallback', { error: primaryError })
const result = await fallbackMethod(request)
return result
} catch (fallbackError) {
// Partial result if possible
const partial = await attemptPartialResult(request)
if (partial) {
send.ServiceResult.deliver({
requestId: request.id,
outputs: partial,
warnings: ['Partial result due to errors'],
quality: 'degraded',
})
return
}
// Final failure with helpful message
send.ServiceRequest.fail({
requestId: request.id,
error: 'Service temporarily unavailable',
retryable: true,
retryAfter: 300, // seconds
supportContact: '[email protected]',
})
}
}
})Quality Assurance
1. Validate Everything
Never trust input data:
async function validateRequest(request: ServiceRequest, service: Service) {
const errors = []
// Type validation
for (const [field, schema] of Object.entries(service.input.schema)) {
const value = request.inputs[field]
if (schema.required && value === undefined) {
errors.push(`Missing required field: ${field}`)
continue
}
if (value !== undefined) {
// Type check
if (typeof value !== schema.type) {
errors.push(`Invalid type for ${field}: expected ${schema.type}, got ${typeof value}`)
}
// Range checks
if (schema.min !== undefined && value < schema.min) {
errors.push(`${field} must be >= ${schema.min}`)
}
if (schema.max !== undefined && value > schema.max) {
errors.push(`${field} must be <= ${schema.max}`)
}
// Enum validation
if (schema.values && !schema.values.includes(value)) {
errors.push(`${field} must be one of: ${schema.values.join(', ')}`)
}
// Custom validation
if (schema.validate) {
const customError = schema.validate(value)
if (customError) {
errors.push(customError)
}
}
}
}
// Business rule validation
if (service.validation?.rules) {
for (const rule of service.validation.rules) {
if (!rule.check(request.inputs)) {
errors.push(rule.message)
}
}
}
return {
valid: errors.length === 0,
errors,
}
}2. Implement Quality Checks
Verify output quality before delivery:
async function qualityCheck(result: any, service: Service) {
const checks = []
// Completeness check
for (const requiredField of service.output.required) {
if (!result[requiredField]) {
checks.push({
check: 'completeness',
field: requiredField,
passed: false,
message: `Missing required output: ${requiredField}`,
})
}
}
// AI quality assessment
if (service.quality?.aiCheck) {
const assessment = await ai.evaluate({
model: 'gpt-5',
output: result,
criteria: service.quality.criteria,
})
checks.push({
check: 'ai-quality',
passed: assessment.score >= service.quality.threshold,
score: assessment.score,
issues: assessment.issues,
})
}
// Specific quality metrics
if (service.type === $.ServiceType.ContentGeneration) {
// Readability
const readability = calculateReadability(result.content)
checks.push({
check: 'readability',
passed: readability >= 60,
score: readability,
})
// Uniqueness
const uniqueness = await checkPlagiarism(result.content)
checks.push({
check: 'uniqueness',
passed: uniqueness >= 0.95,
score: uniqueness,
})
}
const allPassed = checks.every((c) => c.passed)
return {
passed: allPassed,
checks,
overallScore: checks.reduce((sum, c) => sum + (c.score || 0), 0) / checks.length,
}
}3. Comprehensive Testing
Test everything before deployment:
// Unit tests
describe('Email Validator Service', () => {
test('validates correct email', async () => {
const result = await service.validate('[email protected]')
expect(result.valid).toBe(true)
})
test('rejects invalid email', async () => {
const result = await service.validate('invalid-email')
expect(result.valid).toBe(false)
expect(result.reason).toBeDefined()
})
test('handles edge cases', async () => {
const edgeCases = ['[email protected]', '[email protected]', '[email protected]']
for (const email of edgeCases) {
const result = await service.validate(email)
expect(result.valid).toBe(true)
}
})
})
// Integration tests
describe('Email Validator Integration', () => {
test('integrates with SMTP verification', async () => {
const result = await service.validate('[email protected]', {
smtpCheck: true,
})
expect(result.smtpValid).toBeDefined()
})
})
// Load tests
describe('Email Validator Performance', () => {
test('handles concurrent requests', async () => {
const emails = generateTestEmails(1000)
const start = Date.now()
const results = await Promise.all(emails.map((email) => service.validate(email)))
const duration = Date.now() - start
expect(results.length).toBe(1000)
expect(duration).toBeLessThan(10000) // Under 10 seconds
})
})Performance Optimization
1. Caching Strategy
Cache expensive operations:
import { cache } from 'sdk.do'
// Cache configuration
const cacheConfig = {
ttl: 3600, // 1 hour
maxSize: 1000,
strategy: 'lru', // Least Recently Used
}
// Cache expensive AI operations
async function generateWithCache(prompt: string, options: any) {
const cacheKey = `ai:${hash(prompt)}:${hash(options)}`
// Check cache
const cached = await cache.get(cacheKey)
if (cached) {
logger.debug('cache.hit', { key: cacheKey })
return cached
}
// Generate if not cached
logger.debug('cache.miss', { key: cacheKey })
const result = await ai.generate({
model: 'gpt-5',
prompt,
...options,
})
// Store in cache
await cache.set(cacheKey, result, cacheConfig.ttl)
return result
}
// Cache database queries
async function getUserWithCache(userId: string) {
return await cache.getOrSet(
`user:${userId}`,
async () => await db.Users.findOne({ id: userId }),
300 // 5 minutes
)
}
// Smart cache invalidation
on.User.updated(async (user) => {
cache.delete(`user:${user.id}`)
})2. Batch Processing
Process multiple items efficiently:
// Batch similar requests
class RequestBatcher {
private queue: Request[] = []
private timer: NodeJS.Timeout | null = null
private batchSize = 50
private maxWaitTime = 100 // ms
add(request: Request) {
this.queue.push(request)
if (this.queue.length >= this.batchSize) {
this.flush()
} else if (!this.timer) {
this.timer = setTimeout(() => this.flush(), this.maxWaitTime)
}
}
private async flush() {
if (this.timer) {
clearTimeout(this.timer)
this.timer = null
}
if (this.queue.length === 0) return
const batch = this.queue.splice(0)
try {
// Process batch efficiently
const results = await processBatch(batch)
// Deliver individual results
for (let i = 0; i < batch.length; i++) {
await deliverResult(batch[i].id, results[i])
}
} catch (error) {
// Handle batch error
for (const request of batch) {
await deliverError(request.id, error)
}
}
}
}
const batcher = new RequestBatcher()
on.ServiceRequest.created(async (request) => {
if (request.batchable) {
batcher.add(request)
} else {
await processImmediately(request)
}
})3. Async Processing
Use async for long-running tasks:
// Background job queue
import { queue } from 'sdk.do'
on.ServiceRequest.created(async (request) => {
if (estimateProcessingTime(request) > 30000) {
// Long-running task, use queue
await queue.add(
'service-execution',
{
requestId: request.id,
serviceId: request.serviceId,
inputs: request.inputs,
},
{
priority: request.priority || 'normal',
attempts: 3,
backoff: 'exponential',
}
)
// Acknowledge immediately
send.ServiceRequest.acknowledged({
requestId: request.id,
message: 'Request queued for processing',
estimatedTime: estimateProcessingTime(request),
})
} else {
// Quick task, process synchronously
await processImmediately(request)
}
})
// Process background jobs
queue.process('service-execution', async (job) => {
const { requestId, serviceId, inputs } = job.data
try {
const result = await executeService(serviceId, inputs)
send.ServiceResult.deliver({
requestId,
outputs: result,
})
return result
} catch (error) {
if (job.attemptsMade < 3) {
throw error // Will retry
} else {
send.ServiceRequest.fail({
requestId,
error: error.message,
attempts: job.attemptsMade,
})
}
}
})Pricing Strategy
1. Value-Based Pricing
Price based on customer value:
// Calculate value-based price
async function calculatePrice(service: Service, request: ServiceRequest) {
// Estimate value created
const valueEstimate = await estimateValue(service, request)
// Base price on value
let price = valueEstimate.value * service.pricing.valueShare
// Adjust for customer segment
const customer = await db.Customers.findOne({ id: request.customerId })
if (customer.segment === 'enterprise') {
price *= 1.5 // Enterprise premium
} else if (customer.segment === 'startup') {
price *= 0.7 // Startup discount
}
// Volume discounts
const monthlyUsage = await getMonthlyUsage(customer.id, service.id)
if (monthlyUsage > 100) {
price *= 0.9 // 10% volume discount
}
// Apply bounds
price = Math.max(price, service.pricing.minimum)
price = Math.min(price, service.pricing.maximum)
return {
price,
factors: {
baseValue: valueEstimate.value,
valueShare: service.pricing.valueShare,
customerSegment: customer.segment,
volumeDiscount: monthlyUsage > 100,
finalPrice: price,
},
}
}2. Transparent Pricing
Always show customers what they'll pay:
// Price preview
on.ServiceRequest.created(async (request) => {
// Calculate exact price
const pricing = await calculatePrice(service, request)
// Show breakdown to customer
send.Customer.priceEstimate({
customerId: request.customerId,
requestId: request.id,
estimate: {
subtotal: pricing.price,
breakdown: pricing.factors,
tax: pricing.price * 0.08,
total: pricing.price * 1.08,
currency: 'USD',
},
requireApproval: pricing.price > 100,
})
// Wait for approval if needed
if (pricing.price > 100) {
const approved = await waitForApproval(request.id, 300000) // 5 minutes
if (!approved) {
send.ServiceRequest.cancel({
requestId: request.id,
reason: 'Customer did not approve price',
})
return
}
}
// Process request...
})3. Fair Billing
Only charge for successful deliveries:
// Success-based billing
on.ServiceResult.delivered(async (result) => {
// Only charge if successful
if (result.status === 'success') {
send.Payment.charge({
customerId: result.customerId,
amount: result.cost,
description: result.description,
})
}
})
// Refund on failure
on.ServiceRequest.fail(async (request) => {
if (request.paymentId) {
send.Payment.refund({
paymentId: request.paymentId,
reason: 'Service execution failed',
amount: 'full',
})
}
})
// Partial refunds for quality issues
on.QualityIssue.reported(async (issue) => {
if (issue.severity === 'major') {
send.Payment.refund({
paymentId: issue.paymentId,
reason: 'Quality issue',
amount: 'partial',
percentage: 0.5, // 50% refund
})
}
})Security
1. Input Sanitization
Protect against malicious input:
import { sanitize } from 'sdk.do'
function sanitizeInputs(inputs: any) {
const sanitized = {}
for (const [key, value] of Object.entries(inputs)) {
if (typeof value === 'string') {
// Remove dangerous characters
sanitized[key] = sanitize.string(value, {
allowedTags: [],
maxLength: 10000,
})
} else if (typeof value === 'object') {
sanitized[key] = sanitizeInputs(value)
} else {
sanitized[key] = value
}
}
return sanitized
}2. Rate Limiting
Prevent abuse:
import { rateLimit } from 'sdk.do'
// Rate limit configuration
const limiter = rateLimit({
windowMs: 60000, // 1 minute
max: 100, // 100 requests per minute
keyGenerator: (request) => request.customerId,
handler: async (request) => {
send.ServiceRequest.reject({
requestId: request.id,
reason: 'Rate limit exceeded',
retryAfter: 60,
})
},
})
on.ServiceRequest.created(async (request) => {
// Check rate limit
if (!(await limiter.check(request))) {
return
}
// Process request...
})3. Authentication & Authorization
Secure your services:
// Authenticate request
async function authenticate(request: ServiceRequest) {
const token = request.headers['authorization']
if (!token) {
throw new Error('Missing authentication token')
}
const user = await verifyToken(token)
if (!user) {
throw new Error('Invalid authentication token')
}
return user
}
// Authorize access
async function authorize(user: User, service: Service, action: string) {
// Check service access
const hasAccess = await db.ServiceAccess.exists({
userId: user.id,
serviceId: service.id,
action,
})
if (!hasAccess) {
throw new Error('Unauthorized access to service')
}
// Check subscription status
if (service.requiresSubscription) {
const subscription = await db.Subscriptions.findOne({
userId: user.id,
serviceId: service.id,
status: 'active',
})
if (!subscription) {
throw new Error('Active subscription required')
}
}
return true
}Monitoring & Observability
1. Structured Logging
Log everything important:
import { logger } from 'sdk.do'
// Structured logging with context
on.ServiceRequest.created(async (request) => {
const context = {
requestId: request.id,
serviceId: request.serviceId,
customerId: request.customerId,
timestamp: new Date().toISOString(),
}
logger.info('request.start', context)
try {
const result = await executeService(request)
logger.info('request.success', {
...context,
duration: result.duration,
cost: result.cost,
})
} catch (error) {
logger.error('request.failed', {
...context,
error: error.message,
stack: error.stack,
})
}
})2. Comprehensive Metrics
Track everything:
// Key metrics to track
const metrics = {
// Performance
'service.response_time': 'histogram',
'service.throughput': 'counter',
'service.error_rate': 'gauge',
// Business
'service.revenue': 'counter',
'service.customers': 'gauge',
'service.conversions': 'counter',
// Quality
'service.quality_score': 'gauge',
'service.customer_satisfaction': 'gauge',
// Infrastructure
'service.cpu_usage': 'gauge',
'service.memory_usage': 'gauge',
'service.active_requests': 'gauge',
}3. Proactive Alerting
Catch issues early:
// Alert on anomalies
const anomalyDetector = {
check: async (metric: string, value: number) => {
const historical = await getHistoricalData(metric, '7d')
const mean = calculateMean(historical)
const stdDev = calculateStdDev(historical)
// Check if value is anomalous (> 3 std devs)
if (Math.abs(value - mean) > 3 * stdDev) {
send.Alert.create({
severity: 'warning',
title: `Anomaly detected: ${metric}`,
value,
expected: mean,
deviation: Math.abs(value - mean) / stdDev,
})
}
},
}Documentation
1. API Documentation
Document everything:
const service = await $.Service.create({
name: 'Document Summarizer',
documentation: {
description: 'AI-powered document summarization service',
examples: [
{
title: 'Basic usage',
request: {
document: 'Long document text...',
maxLength: 500,
},
response: {
summary: 'Concise summary...',
keyPoints: ['Point 1', 'Point 2'],
confidence: 0.95,
},
},
],
errors: [
{
code: 'DOCUMENT_TOO_SHORT',
message: 'Document must be at least 100 words',
resolution: 'Provide a longer document',
},
{
code: 'INVALID_STYLE',
message: 'Invalid summary style',
resolution: 'Use one of: concise, detailed, bullet-points',
},
],
rateLimit: '100 requests per minute',
pricing: '$0.01 per 100 words',
averageResponseTime: '5 seconds',
},
})2. User Guides
Help customers succeed:
- Clear onboarding
- Step-by-step tutorials
- Common use cases
- Troubleshooting guides
- Best practices
3. Changelog
Track changes:
const changelog = [
{
version: '1.2.0',
date: '2025-10-25',
changes: ['Added support for PDF documents', 'Improved summary quality by 15%', 'Fixed issue with long documents'],
},
{
version: '1.1.0',
date: '2025-10-15',
changes: ['Added bullet-point summary style', 'Reduced average response time by 20%'],
},
]Summary
Key principles for successful services:
- Focus: Do one thing exceptionally well
- Quality: Validate inputs, verify outputs
- Performance: Cache, batch, async
- Pricing: Fair, transparent, value-based
- Security: Sanitize, limit, authenticate
- Monitoring: Log, measure, alert
- Documentation: Clear, comprehensive, current
Next Steps
- Build Your Service → - Start implementing
- See Examples → - Learn from real services
- Deploy to Production → - Go live