Skip to main content

Best Practices Overview

This section provides comprehensive guidelines for developing high-quality, secure, and performant Qirvo plugins. The best practices are organized into focused areas to help you build production-ready plugins.

Table of Contents

Core Development Practices

1. Follow TypeScript Standards

Always use TypeScript for type safety and better development experience:

// ✅ Good: Proper typing
interface PluginConfig {
apiKey: string;
refreshInterval: number;
enableNotifications: boolean;
}

export default class MyPlugin extends BasePlugin {
private config: PluginConfig;

async onEnable(context: PluginRuntimeContext): Promise<void> {
this.config = this.validateConfig(context.config);
}

private validateConfig(rawConfig: any): PluginConfig {
return {
apiKey: rawConfig.apiKey || '',
refreshInterval: rawConfig.refreshInterval || 300,
enableNotifications: rawConfig.enableNotifications ?? true
};
}
}

2. Implement Proper Error Handling

Always handle errors gracefully and provide meaningful feedback:

// ✅ Good: Comprehensive error handling
async fetchExternalData(): Promise<ApiResponse> {
try {
const response = await this.context.api.http.get('/api/data');
return await response.json();
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
this.log('warn', 'Network unavailable, using cached data');
return await this.getCachedData();
}

this.log('error', 'Failed to fetch data:', error);
throw new PluginError('Data fetch failed', 'FETCH_ERROR', true);
}
}

3. Use Proper Logging

Implement structured logging with appropriate levels:

// ✅ Good: Structured logging
this.log('info', 'Plugin initialized', {
version: this.context.plugin.version,
userId: this.context.user?.id,
timestamp: new Date().toISOString()
});

this.log('debug', 'API request details', {
url: '/api/endpoint',
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});

4. Manage Resources Properly

Always clean up resources to prevent memory leaks:

// ✅ Good: Resource management
export default class ResourceAwarePlugin extends BasePlugin {
private timers: NodeJS.Timeout[] = [];
private connections: Connection[] = [];

async onEnable(context: PluginRuntimeContext): Promise<void> {
const timer = setInterval(() => this.refresh(), 30000);
this.timers.push(timer);
}

async onDisable(): Promise<void> {
// Clean up timers
this.timers.forEach(timer => clearInterval(timer));
this.timers = [];

// Close connections
await Promise.all(this.connections.map(conn => conn.close()));
this.connections = [];
}
}

Specialized Best Practice Guides

Security Best Practices

📖 Security Guidelines

  • Input validation and sanitization
  • Secure credential storage
  • Permission management
  • XSS and injection prevention
  • Secure communication protocols

Performance Optimization

📖 Performance Guidelines

  • Caching strategies
  • Resource optimization
  • Memory management
  • Lazy loading techniques
  • Background task optimization

Code Quality Standards

📖 Code Quality Guidelines

  • Code organization and structure
  • Naming conventions
  • Documentation standards
  • Testing strategies
  • Code review practices

User Experience Guidelines

📖 UX Guidelines

  • Interface design principles
  • Accessibility standards
  • Error messaging
  • Loading states
  • User feedback patterns

Deployment and Distribution

📖 Deployment Guidelines

  • Plugin packaging
  • Version management
  • Update strategies
  • Marketplace submission
  • Distribution channels

Quick Reference

Essential Checklist

Before releasing your plugin, ensure you've followed these critical practices:

Security

  • All user inputs are validated and sanitized
  • Sensitive data is encrypted before storage
  • Permissions follow principle of least privilege
  • External API calls use secure authentication

Performance

  • Implement caching for expensive operations
  • Use lazy loading for non-critical features
  • Clean up resources in lifecycle hooks
  • Optimize bundle size and dependencies

Code Quality

  • TypeScript strict mode enabled
  • Comprehensive error handling implemented
  • Proper logging with appropriate levels
  • Unit tests cover critical functionality

User Experience

  • Clear error messages for users
  • Loading states for async operations
  • Graceful degradation when features fail
  • Accessible UI components

Documentation

  • README with installation and usage instructions
  • API documentation for public methods
  • Configuration options documented
  • Changelog maintained for versions

Common Anti-Patterns to Avoid

Don't Do This

// ❌ Bad: No error handling
async fetchData() {
const response = await fetch('/api/data');
return response.json();
}

// ❌ Bad: Hardcoded values
const API_KEY = 'abc123';
const REFRESH_INTERVAL = 5000;

// ❌ Bad: No resource cleanup
setInterval(() => {
this.updateData();
}, 1000);

// ❌ Bad: Synchronous operations blocking
const data = fs.readFileSync('large-file.json');

Do This Instead

// ✅ Good: Proper error handling
async fetchData(): Promise<ApiData> {
try {
const response = await this.context.api.http.get('/api/data');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
this.log('error', 'Data fetch failed:', error);
throw new PluginError('Failed to fetch data', 'FETCH_ERROR');
}
}

// ✅ Good: Configuration-driven
private get apiKey(): string {
return this.config.apiKey || process.env.PLUGIN_API_KEY || '';
}

private get refreshInterval(): number {
return this.config.refreshInterval || 30000;
}

// ✅ Good: Managed resources
async onEnable(): Promise<void> {
this.refreshTimer = setInterval(
() => this.updateData(),
this.refreshInterval
);
}

async onDisable(): Promise<void> {
if (this.refreshTimer) {
clearInterval(this.refreshTimer);
this.refreshTimer = null;
}
}

// ✅ Good: Asynchronous operations
async loadData(): Promise<void> {
try {
const data = await this.context.storage.get('large-data');
this.processData(data);
} catch (error) {
this.log('error', 'Failed to load data:', error);
}
}

Performance Guidelines Summary

  1. Cache Frequently Used Data: Store API responses and computed results
  2. Use Lazy Loading: Load features only when needed
  3. Optimize Bundle Size: Remove unused dependencies
  4. Implement Debouncing: For user input and frequent operations
  5. Monitor Memory Usage: Clean up references and event listeners

Security Guidelines Summary

  1. Validate All Inputs: Never trust user or external data
  2. Use HTTPS Only: For all external communications
  3. Store Secrets Securely: Encrypt sensitive configuration
  4. Follow OWASP Guidelines: For web security best practices
  5. Regular Security Audits: Review dependencies and code

Next Steps: Choose a specific best practice guide from the list above to dive deeper into that area, or continue with the Testing Guide to learn about testing your plugins.