When profiling an Electron + React + Node.js monorepo for performance, follow these steps in order. Measure before recommending fixes.
# Build the web UI and check output size
pnpm build:web
du -sh apps/web/dist/
# Analyze bundle composition
npx vite-bundle-visualizer --config apps/web/vite.config.ts
# Or if rollup-plugin-visualizer is installed:
# pnpm -F @pvybutler/web build -- --analyze
rg "import \* as" --type ts --type tsx apps/web/src/rg "import(" --type ts --type tsx apps/web/src/# Measure daemon cold start
time pnpm dev:web
# Note: first paint time in the terminal output
# Or more precisely:
node -e "
const start = Date.now();
require('./apps/daemon/dist/index.js');
console.log('Daemon started in', Date.now() - start, 'ms');
"
Read apps/desktop/src/main/ipc/handlers.ts:
invoke calls# In the renderer, add timing:
# console.time('ipc:getTasks'); await window.accomplish.getTasks(); console.timeEnd('ipc:getTasks');
# Start the daemon and measure route response times
pnpm dev:web &
sleep 5
# Measure each route
for route in /health /api/tasks /api/settings; do
echo -n "$route: "
curl -s -o /dev/null -w "%{time_total}s\n" "http://localhost:3001$route"
done
Read apps/daemon/src/scheduler-service.ts:
Read packages/agent-core/src/storage/migrations/:
# Check Electron memory usage
# In DevTools: Performance > Memory
# Or in terminal:
ps aux | grep -i electron | awk '{print $4, $11}'
Read apps/daemon/src/task-event-forwarding.ts:
Record all measurements in this format:
## Performance Baseline
| Metric | Value | Target | Status |
|---|---|---|---|
| Web bundle (gzip) | ___ KB | <500 KB | |
| Web bundle (raw) | ___ KB | <2000 KB | |
| Daemon cold start | ___ ms | <2000 ms | |
| First paint (Electron) | ___ ms | <3000 ms | |
| IPC round-trip (avg) | ___ ms | <50 ms | |
| Route latency (avg) | ___ ms | <100 ms | |
| Memory (main process) | ___ MB | <200 MB | |
| Memory (renderer) | ___ MB | <300 MB | |
Then prioritize optimizations by impact: fix the metric that’s furthest from target first.