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
- Input validation and sanitization
- Secure credential storage
- Permission management
- XSS and injection prevention
- Secure communication protocols
Performance Optimization
- Caching strategies
- Resource optimization
- Memory management
- Lazy loading techniques
- Background task optimization
Code Quality Standards
- Code organization and structure
- Naming conventions
- Documentation standards
- Testing strategies
- Code review practices
User Experience Guidelines
- Interface design principles
- Accessibility standards
- Error messaging
- Loading states
- User feedback patterns
Deployment and Distribution
- 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
- Cache Frequently Used Data: Store API responses and computed results
- Use Lazy Loading: Load features only when needed
- Optimize Bundle Size: Remove unused dependencies
- Implement Debouncing: For user input and frequent operations
- Monitor Memory Usage: Clean up references and event listeners
Security Guidelines Summary
- Validate All Inputs: Never trust user or external data
- Use HTTPS Only: For all external communications
- Store Secrets Securely: Encrypt sensitive configuration
- Follow OWASP Guidelines: For web security best practices
- 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.