From a 601KB monstrosity to a lightning-fast 24.75KB extension - here are the hard-earned performance secrets that transformed my VS Code extension from sluggish to snappy.
The Performance Wake-Up Call π
When my extension hit 601KB and took 19 seconds to build, I realized: Performance isn't optional. It's the difference between a tool people try and a tool people depend on.
Here are the 5 secrets that changed everything.
Secret #1: Bundle Size Is User Experience π¦
The Problem
β Before: 601KB bundle
β‘ After: 24.75KB bundle
π Result: 95.9% size reduction
Why this matters: Every KB affects activation time, memory usage, and user perception.
The Solution: Strategic Dependencies
// β Heavy approach - 400KB+ bundle impact
import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
// β
Lightweight approach - ~2KB impact
const functionPattern = /^(\s*)(?:export\s+)?(async\s+)?function\s+(\w+)/gm;
Key insight: Sometimes the "wrong" solution (regex) beats the "right" solution (AST) when performance matters.
Action Items
- Audit dependencies with
npm ls --depth=0
- Question every import: "Do I really need this?"
- Use lighter alternatives when possible
Secret #2: Build Speed Determines Development Quality β‘
The Problem
19-second builds meant I couldn't iterate quickly, leading to:
- Slower bug fixes
- Less experimental features
- Frustrated development experience
The Solution: esbuild Over Webpack
// esbuild.config.js - The game changer
const esbuild = require('esbuild');
await esbuild.build({
entryPoints: ['src/extension.ts'],
bundle: true,
minify: true,
platform: 'node',
format: 'cjs',
external: ['vscode'],
outfile: 'dist/extension.js'
});
Results:
- Build time: 19s β 0.8s (20x faster)
- Watch mode: Near instant rebuilds
- Development happiness: Through the roof
Action Items
- Try esbuild for your next project
- Measure build times and set limits
- Fast builds enable better software
Secret #3: Lazy Loading Saves Activation Time π¦₯
The Problem
Loading everything at activation killed startup performance.
The Solution: Load Only What's Needed
// β Eager loading - slow activation
import * as allServices from './services';
export function activate() {
const everything = new allServices.EverythingManager();
}
// β
Lazy loading - instant activation
export function activate() {
// Only register commands, load services when needed
vscode.commands.registerCommand('extension.action', async () => {
const { ServiceManager } = await import('./services/ServiceManager');
const service = ServiceManager.getInstance();
return service.execute();
});
}
Key insight: Register commands immediately, import heavy modules only when commands execute.
Secret #4: Efficient File Operations Scale π
The Problem
Naive file operations became bottlenecks with large projects.
The Solution: Smart Caching and Batching
// β Inefficient - reads package.json every time
export function detectProject(): ProjectType {
const packageJson = fs.readFileSync('package.json', 'utf8');
return analyzeProject(JSON.parse(packageJson));
}
// β
Efficient - cache and batch
export class ProjectDetectionService {
private cache = new Map<string, ProjectType>();
public detectProject(workspaceRoot: string): ProjectType {
if (this.cache.has(workspaceRoot)) {
return this.cache.get(workspaceRoot)!;
}
const result = this.expensiveDetection(workspaceRoot);
this.cache.set(workspaceRoot, result);
return result;
}
}
Performance impact: 10x faster repeated operations in large workspaces.
Secret #5: Memory Management Prevents Crashes π§
The Problem
VS Code extensions share memory with the editor and other extensions. Memory leaks = user frustration.
The Solution: Proper Cleanup
// β
Always clean up subscriptions
export function activate(context: vscode.ExtensionContext) {
const disposables: vscode.Disposable[] = [];
// Register disposables
disposables.push(
vscode.commands.registerCommand('ext.command', handler),
vscode.workspace.onDidChangeConfiguration(configHandler),
vscode.window.onDidChangeActiveTextEditor(editorHandler)
);
// Ensure cleanup
context.subscriptions.push(...disposables);
}
// β
Singleton pattern for shared resources
export class ServiceManager {
private static instance: ServiceManager;
public static getInstance(): ServiceManager {
if (!ServiceManager.instance) {
ServiceManager.instance = new ServiceManager();
}
return ServiceManager.instance;
}
}
Why this works: One instance per VS Code session, automatic cleanup on deactivation.
Bonus Performance Tips π‘
Measure Everything
# Use VS Code's built-in performance tools
code --enable-extension-host-logging
Set Performance Budgets
// Example performance constraints I use
const PERFORMANCE_BUDGETS = {
bundleSize: 50_000, // 50KB max
activationTime: 500, // 500ms max
buildTime: 5_000, // 5s max
};
Test with Real Data
Don't just test with your perfect demo project. Test with:
- Large codebases (1000+ files)
- Deeply nested structures
- Concurrent operations
- Edge cases that stress your algorithms
The Results That Matter π
After applying these secrets:
User Experience:
- "Feels like it's built into VS Code"
- "Loads instantly, works perfectly"
- "Finally, an extension that doesn't slow things down"
Developer Experience:
- 20x faster builds enable rapid iteration
- Memory-efficient architecture prevents crashes
- Cached operations make large projects feel responsive
Your Performance Action Plan β
- Audit your bundle - What's actually in there?
- Switch to esbuild - 10x build speed improvements are common
- Implement lazy loading - Defer expensive imports
- Add caching - Cache expensive operations
- Measure relentlessly - Set budgets and stick to them
The Bottom Line π―
Performance is a feature. Users notice sluggishness before they notice capabilities.
A fast, reliable extension with 3 features beats a slow extension with 30 features every time.
Your users' time is precious. Honor it with software that doesn't waste it.
Want to see these secrets in action? Check out Additional Context Menus - built with every one of these performance optimizations!
Building your own extension? Which performance secret surprised you most? Share your optimization wins in the comments! π
Top comments (0)