| name | perf-optimizer |
| description | Identify and fix performance bottlenecks in Kibana build, test, and CI workflows by analyzing slow operations, diagnosing root causes, and measuring optimization impact. |
Performance Optimizer
Description
Identifies and fixes performance bottlenecks in Kibana build, test, and CI workflows. Analyzes slow operations, diagnoses root causes, suggests targeted optimizations, and measures impact.
Triggers
- "optimize build time"
- "why is this test slow"
- "reduce CI time"
- "analyze performance"
- "speed up [build|test|CI]"
- "profile [webpack|jest|scout]"
Core Capabilities
1. Detect Slow Operations
- Build analysis: Webpack bundle size, compilation time, plugin loading
- Test profiling: Jest/Scout execution time, setup/teardown duration
- CI timing: Buildkite agent hours, step duration, parallelism efficiency
- Bootstrap analysis:
yarn kbn bootstrap dependency resolution
2. Analyze Bottlenecks
- Bundle bloat: Large packages (>5MB), duplicate dependencies, dead code
- Test inefficiency: Expensive setup (>30s), redundant ES archive loads, serial execution
- CI waste: Redundant steps, under-utilized parallelism, cache misses
- Startup overhead: Lazy loadable code, synchronous imports, unused plugins
3. Suggest Optimizations
- Code splitting: Dynamic imports, route-based chunks, lazy components
- Caching: Webpack cache, Jest cache, Scout ES archive snapshots
- Parallelization: Increase Jest/Scout workers, Buildkite agent count
- Tree-shaking: Replace lodash with lodash-es, eliminate side effects
- Global setup: Shared test fixtures, reusable ES instances
4. Measure Impact
- Before/after metrics: Timing, bundle size, agent-hours
- Cost analysis: Agent-hour cost, developer time saved
- ROI calculation: One-time effort vs ongoing savings
Instructions
Phase 1: Detection & Diagnosis
When user requests performance analysis:
-
Identify scope
- Build performance → webpack analysis
- Test performance → Jest/Scout profiling
- CI performance → Buildkite analytics
- Bootstrap performance → dependency graph analysis
-
Gather baseline metrics
STATS_JSON=true node scripts/build_kibana_platform_plugins.js --focus <plugin>
yarn test:jest --config <config> --verbose --detectOpenHandles
-
Run automated analysis
- Use
webpack-bundle-analyzer for bundle visualization
- Parse Jest
--json output for slow tests
- Check CI logs for repeated expensive operations
- Profile Node.js with
--cpu-prof if needed
Phase 2: Root Cause Analysis
Bundle Bloat Patterns
Test Inefficiency Patterns
CI Waste Patterns
Phase 3: Optimization Strategies
Build Optimizations
1. Code Splitting
import { HeavyComponent } from './heavy_component';
const HeavyComponent = lazy(() => import('./heavy_component'));
2. Tree-Shaking
import _ from 'lodash';
import { debounce, throttle } from 'lodash-es';
3. Webpack Cache
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
};
4. Plugin Lazy Loading
{
"type": "plugin",
"plugin": {
"id": "myPlugin",
"requiredPlugins": ["data"],
"optionalPlugins": ["maps", "lens"]
}
}
Test Optimizations
1. Shared ES Archives
beforeEach(async () => {
await esArchiver.load('security_solution/alerts');
});
beforeAll(async () => {
await esArchiver.load('security_solution/alerts');
});
afterAll(async () => {
await esArchiver.unload('security_solution/alerts');
});
2. Increase Parallelism
module.exports = {
maxWorkers: 1,
maxWorkers: '50%',
};
export default {
workers: 1,
workers: Math.max(1, Math.floor(os.cpus().length * 0.75)),
};
3. Global Setup Hook
module.exports = {
globalSetup: '<rootDir>/global_setup.ts',
globalTeardown: '<rootDir>/global_teardown.ts',
};
export default async () => {
const es = await startElasticsearch();
process.env.ES_URL = es.url;
};
4. Scout ES Snapshot Caching
test.beforeAll(async ({ kbnClient }) => {
await kbnClient.importExport.load(...);
await kbnClient.savedObjects.create(...);
});
CI Optimizations
1. Cache Bootstrap
steps:
- label: "Bootstrap"
command: "yarn kbn bootstrap"
plugins:
- cache#v1:
key: "v1-yarn-{{ checksum 'yarn.lock' }}"
paths:
- "node_modules"
- ".yarn/cache"
2. Parallelize Tests
steps:
- label: "Test Suite"
command: "yarn test:jest"
steps:
- label: "Test Suite"
command: "yarn test:jest"
parallelism: 10
3. Incremental Type Checking
steps:
- label: "Type Check"
command: |
CHANGED_FILES=$(git diff --name-only origin/main...HEAD | grep '\.tsx\?$')
for file in $CHANGED_FILES; do
# Find nearest tsconfig
TSCONFIG=$(find $(dirname $file) -name tsconfig.json -type f | head -n1)
yarn test:type_check --project $TSCONFIG
done
4. Conditional Steps
steps:
- label: "API Tests"
if: |
git diff --name-only origin/main...HEAD | grep -E "src/.*\.ts$"
command: "yarn test:jest --config jest.integration.config.js"
Phase 4: Impact Measurement
Metrics to Track
-
Build Performance
- Bundle size (MB): Before vs After
- Compilation time (seconds): Before vs After
- First load time (seconds): Before vs After
-
Test Performance
- Suite duration (minutes): Before vs After
- Test count: No change expected
- Failure rate: No change expected
-
CI Performance
- Pipeline duration (minutes): Before vs After
- Agent hours consumed: Before vs After
- Cost ($): Before vs After (agent-hour rate)
-
Developer Experience
- Local build time: Before vs After
- Test feedback loop: Before vs After
- Merge queue time: Before vs After
Measurement Template
## Performance Optimization Results
### Scope
- **Target**: [Build/Test/CI]
- **Package**: [Plugin/Package name]
- **Date**: [YYYY-MM-DD]
### Baseline (Before)
- Metric 1: [value + unit]
- Metric 2: [value + unit]
- Metric 3: [value + unit]
### Optimizations Applied
1. [Optimization name]: [Brief description]
2. [Optimization name]: [Brief description]
### Results (After)
- Metric 1: [value + unit] (**-XX%**)
- Metric 2: [value + unit] (**-XX%**)
- Metric 3: [value + unit] (**-XX%**)
### Cost-Benefit Analysis
- **Implementation time**: X hours
- **Time saved per build/test/CI run**: Y minutes
- **Runs per day**: Z
- **Total time saved per day**: Y * Z minutes
- **ROI breakeven**: X hours / (Y * Z hours/day) = N days
### Recommendations
- [Next optimization opportunity]
- [Monitoring to prevent regression]
Phase 5: Implementation & Validation
-
Create optimization branch
git checkout -b perf/optimize-<target>-<date>
-
Apply optimizations incrementally
- One optimization per commit
- Measure impact after each change
- Document reasoning in commit message
-
Validate no regressions
STATS_JSON=true node scripts/build_kibana_platform_plugins.js
yarn test:jest --config <config>
yarn test:type_check --project <tsconfig>
node scripts/eslint --fix $(git diff --name-only)
-
Create PR with metrics
- Include before/after comparison
- Link to Buildkite builds showing improvement
- Document any trade-offs (e.g., lazy loading = slight delay on first use)
Kibana-Specific Patterns
Bootstrap Optimization
yarn config set nodeLinker pnp
yarn install
Scout Parallelism
import os from 'os';
export default {
workers: Math.max(1, os.cpus().length - 2),
fullyParallel: false,
reuseExistingServer: !process.env.CI,
};
ES Archive Caching
import { test } from '@kbn/scout';
test.describe('Alerts', () => {
test.beforeAll(async ({ kbnClient, esClient }) => {
await esClient.indices.create({ index: 'alerts' });
await kbnClient.savedObjects.create(...);
});
test.afterAll(async ({ esClient }) => {
await esClient.indices.delete({ index: 'alerts' });
});
test('test 1', async () => { ... });
test('test 2', async () => { ... });
});
Plugin Lazy Loading
{
"type": "plugin",
"plugin": {
"id": "myPlugin",
"requiredPlugins": ["data"],
"optionalPlugins": ["maps", "lens", "ml"],
"requiredBundles": ["kibanaReact"]
}
}
export class MyPlugin {
setup(core, plugins) {
core.http.route({
path: '/app/my-plugin/map',
handler: async () => {
const maps = await core.plugins.getPlugin('maps');
return maps ? maps.renderMap() : null;
},
});
}
}
Example Workflows
Example 1: Optimize Security Solution Build
User Input: "Why is the Security Solution build so slow?"
Agent Actions:
-
Generate webpack stats
cd x-pack/solutions/security
STATS_JSON=true node scripts/build_kibana_platform_plugins.js --focus securitySolution
-
Analyze bundle
npx webpack-bundle-analyzer webpack-stats.json
-
Findings:
- lodash imported 47 times (450KB)
- moment.js with all locales (160KB)
- react-beautiful-dnd duplicated (120KB)
-
Optimizations:
- Replace
import _ from 'lodash' with import { debounce } from 'lodash-es'
- Add webpack plugin:
new MomentLocalesPlugin({ localesToKeep: ['en'] })
- Run
yarn dedupe to remove duplicate react-beautiful-dnd
-
Results:
- Bundle size: 8.2MB → 5.8MB (-29%)
- Build time: 4m 20s → 3m 10s (-27%)
- First load time: 3.2s → 2.1s (-34%)
Example 2: Speed Up Scout Tests
User Input: "reduce CI time for detection engine tests"
Agent Actions:
-
Profile test suite
node scripts/scout run-tests \
--arch stateful \
--domain security \
--config x-pack/test/security_solution_scout/detection_engine.scout.config.ts \
--reporter html
-
Findings (from HTML report timeline):
- 45 tests, 12m 30s total
- Each test loads ES archive (5s per test = 3m 45s wasted)
- Running on 1 worker (serial execution)
- Browser launched 45 times (20s startup overhead)
-
Optimizations:
- Move ES archive load to
beforeAll (5s once vs 5s * 45)
- Increase workers from 1 to 4 (4x parallelism)
- Enable browser context reuse
-
Code changes:
export default {
workers: 4,
fullyParallel: false,
};
test.beforeAll(async ({ esClient, kbnClient }) => {
await esArchiver.load('security_solution/detection_engine');
});
test.afterAll(async ({ esClient }) => {
await esArchiver.unload('security_solution/detection_engine');
});
-
Results:
- Suite duration: 12m 30s → 3m 45s (-70%)
- CI agent hours: 12.5 → 3.75 (-70%)
- Cost savings: $0.50/agent-hour * 8.75 hours = $4.38 per run
Example 3: Optimize CI Pipeline
User Input: "analyze performance of platform builds"
Agent Actions:
-
Query Buildkite API for recent builds
query {
pipeline(slug: "elastic/kibana") {
builds(first: 20, branch: "main") {
edges {
node {
jobs {
... on JobTypeCommand {
label
runnableAt
finishedAt
}
}
}
}
}
}
}
-
Findings:
- "Bootstrap" step: 8m 30s (runs on every agent)
- "Type Check" step: 12m (checks all packages, not just changed)
- Tests run serially: 45m total (could parallelize)
- Artifacts uploaded unnecessarily: 2m overhead
-
Optimizations:
- Cache bootstrap (see CI Optimizations above)
- Incremental type checking (only changed packages)
- Parallelize tests across 10 agents
- Skip artifact upload for non-release builds
-
Results:
- Pipeline duration: 58m → 22m (-62%)
- Agent hours: 58 → 30 (-48%)
- Cost savings: $0.50/hour * 28 hours = $14 per build
- Estimated monthly savings: $14 * 100 builds = $1,400/month
Validation Checklist
Before declaring optimization complete:
Edge Cases & Warnings
Don't Over-Optimize
- Optimizing code that runs once (e.g., one-time migration) has low ROI
- Prefer simple, readable code over premature optimization
- Profile first, optimize second
Watch for Regressions
- Lazy loading can introduce UI delays (first load slower)
- Aggressive caching can mask bugs (stale data)
- Parallel tests can introduce flakiness (race conditions)
- Code splitting can break source maps (harder debugging)
Kibana-Specific Gotchas
- Bootstrap cache must be invalidated when dependencies change
- Scout snapshots don't work with ES cross-cluster search
- Webpack cache breaks when plugins change (must clear)
- Plugin lazy loading requires careful dependency management
Success Criteria
Optimization is successful when:
- Metrics improve by ≥20% (anything less may not be worth effort)
- No regressions in functionality or test coverage
- ROI breakeven within 2 weeks (implementation time recovered)
- Monitoring in place to prevent future regression
- Documentation updated so others can apply same patterns
References