From kibana-build-performance-tools
Identify and fix performance bottlenecks in Kibana build, test, and CI workflows by analyzing slow operations, diagnosing root causes, and measuring optimization impact.
How this skill is triggered — by the user, by Claude, or both
Slash command
/kibana-build-performance-tools:perf-optimizerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Identifies and fixes performance bottlenecks in Kibana build, test, and CI workflows. Analyzes slow operations, diagnoses root causes, suggests targeted optimizations, and measures impact.
Identifies and fixes performance bottlenecks in Kibana build, test, and CI workflows. Analyzes slow operations, diagnoses root causes, suggests targeted optimizations, and measures impact.
yarn kbn bootstrap dependency resolutionWhen user requests performance analysis:
Identify scope
Gather baseline metrics
# Build: Generate webpack stats
STATS_JSON=true node scripts/build_kibana_platform_plugins.js --focus <plugin>
# Test: Profile Jest with --verbose --detectOpenHandles
yarn test:jest --config <config> --verbose --detectOpenHandles
# Scout: Check test timings in Scout HTML report
# Look for tests with >30s duration in timeline
# CI: Query Buildkite GraphQL API for recent builds
# Group by step, calculate p50/p95/p99 timing
Run automated analysis
webpack-bundle-analyzer for bundle visualization--json output for slow tests--cpu-prof if needed# Find large dependencies
cat webpack-stats.json | jq '.assets | sort_by(.size) | reverse | .[0:10]'
# Check for duplicate packages
yarn dedupe --check
# Analyze specific plugin
npx webpack-bundle-analyzer webpack-stats.json
beforeEach (should be in beforeAll)# Profile Jest test timing
yarn test:jest --config <config> --verbose 2>&1 | grep "PASS\|FAIL" | awk '{print $2, $NF}'
# Check Scout parallelism
grep "workers:" <scout_config>
# Identify expensive setup
# Look for "beforeEach" with >1s operations
grep -r "beforeEach" --include="*.test.ts" | xargs grep -l "es.indices.create\|kibanaServer.importExport"
# Check Buildkite step timing
buildkite-agent step get <step-key> --format json | jq '.duration'
# Find steps running bootstrap
grep -r "yarn kbn bootstrap" .buildkite/
# Check parallelism
grep "parallelism:" .buildkite/pipeline.yml
1. Code Splitting
// Before: Synchronous import (bloats main bundle)
import { HeavyComponent } from './heavy_component';
// After: Dynamic import (separate chunk)
const HeavyComponent = lazy(() => import('./heavy_component'));
2. Tree-Shaking
// Before: Imports entire lodash (450KB)
import _ from 'lodash';
// After: Tree-shakeable imports (20KB)
import { debounce, throttle } from 'lodash-es';
3. Webpack Cache
// webpack.config.js
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
},
};
4. Plugin Lazy Loading
// kibana.jsonc
{
"type": "plugin",
"plugin": {
"id": "myPlugin",
"requiredPlugins": ["data"],
// Only load when route accessed
"optionalPlugins": ["maps", "lens"]
}
}
1. Shared ES Archives
// Before: Each test loads archive (5s per test)
beforeEach(async () => {
await esArchiver.load('security_solution/alerts');
});
// After: Load once, reuse across tests (5s total)
beforeAll(async () => {
await esArchiver.load('security_solution/alerts');
});
afterAll(async () => {
await esArchiver.unload('security_solution/alerts');
});
2. Increase Parallelism
// jest.config.js
module.exports = {
// Before: 1 worker (serial)
maxWorkers: 1,
// After: 50% of CPU cores
maxWorkers: '50%',
};
// scout.config.ts
export default {
// Before: 1 worker
workers: 1,
// After: Match available CPU cores
workers: Math.max(1, Math.floor(os.cpus().length * 0.75)),
};
3. Global Setup Hook
// jest.config.js
module.exports = {
globalSetup: '<rootDir>/global_setup.ts',
globalTeardown: '<rootDir>/global_teardown.ts',
};
// global_setup.ts
export default async () => {
// Start ES once for all tests
const es = await startElasticsearch();
process.env.ES_URL = es.url;
};
4. Scout ES Snapshot Caching
// Use Scout's built-in snapshot system
test.beforeAll(async ({ kbnClient }) => {
// Load data once, snapshot the ES state
await kbnClient.importExport.load(...);
await kbnClient.savedObjects.create(...);
});
// Subsequent tests restore from snapshot (faster)
1. Cache Bootstrap
# .buildkite/pipeline.yml
steps:
- label: "Bootstrap"
command: "yarn kbn bootstrap"
# Cache node_modules across builds
plugins:
- cache#v1:
key: "v1-yarn-{{ checksum 'yarn.lock' }}"
paths:
- "node_modules"
- ".yarn/cache"
2. Parallelize Tests
# Before: Serial execution
steps:
- label: "Test Suite"
command: "yarn test:jest"
# After: Parallel execution
steps:
- label: "Test Suite"
command: "yarn test:jest"
parallelism: 10
# Split tests across 10 agents
3. Incremental Type Checking
# Only check changed files
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
# Skip tests if no code changed
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"
Build Performance
Test Performance
CI Performance
Developer Experience
## 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]
Create optimization branch
git checkout -b perf/optimize-<target>-<date>
Apply optimizations incrementally
Validate no regressions
# Build: Verify bundle size
STATS_JSON=true node scripts/build_kibana_platform_plugins.js
# Test: Verify all tests pass
yarn test:jest --config <config>
# Type check
yarn test:type_check --project <tsconfig>
# Lint
node scripts/eslint --fix $(git diff --name-only)
Create PR with metrics
# Problem: Bootstrap takes 10+ minutes
# Cause: Re-resolves dependencies every time
# Solution: Use Yarn PnP (Plug'n'Play)
yarn config set nodeLinker pnp
yarn install
# Or: Cache node_modules in CI (see CI Optimizations above)
// scout.config.ts
import os from 'os';
export default {
// Match CPU cores (leave 2 for system)
workers: Math.max(1, os.cpus().length - 2),
// Reuse browser contexts (faster than fresh launch)
fullyParallel: false,
// Keep browser alive between tests
reuseExistingServer: !process.env.CI,
};
// Use Scout's snapshot system instead of manual archiving
import { test } from '@kbn/scout';
test.describe('Alerts', () => {
test.beforeAll(async ({ kbnClient, esClient }) => {
// Load data once
await esClient.indices.create({ index: 'alerts' });
await kbnClient.savedObjects.create(...);
// Scout automatically snapshots ES state
});
test.afterAll(async ({ esClient }) => {
// Cleanup
await esClient.indices.delete({ index: 'alerts' });
});
// Each test gets fresh ES state from snapshot (fast)
test('test 1', async () => { ... });
test('test 2', async () => { ... });
});
// Only load expensive plugins when needed
// kibana.jsonc
{
"type": "plugin",
"plugin": {
"id": "myPlugin",
"requiredPlugins": ["data"],
// Lazy load (don't block startup)
"optionalPlugins": ["maps", "lens", "ml"],
"requiredBundles": ["kibanaReact"]
}
}
// In plugin code
export class MyPlugin {
setup(core, plugins) {
// Register route that lazy loads Maps
core.http.route({
path: '/app/my-plugin/map',
handler: async () => {
// Only load Maps when this route accessed
const maps = await core.plugins.getPlugin('maps');
return maps ? maps.renderMap() : null;
},
});
}
}
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:
Optimizations:
import _ from 'lodash' with import { debounce } from 'lodash-es'new MomentLocalesPlugin({ localesToKeep: ['en'] })yarn dedupe to remove duplicate react-beautiful-dndResults:
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):
Optimizations:
beforeAll (5s once vs 5s * 45)Code changes:
// detection_engine.scout.config.ts
export default {
workers: 4, // Was: 1
fullyParallel: false, // Reuse browser contexts
};
// detection_engine.test.ts
test.beforeAll(async ({ esClient, kbnClient }) => {
// Load once
await esArchiver.load('security_solution/detection_engine');
});
test.afterAll(async ({ esClient }) => {
await esArchiver.unload('security_solution/detection_engine');
});
Results:
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:
Optimizations:
Results:
Before declaring optimization complete:
Optimization is successful when:
npx claudepluginhub patrykkopycinski/patryks-treadmill-claude-plugins --plugin kibana-build-performance-toolsGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.