Performance Optimization Strategies
Optimise intentionally: profile first, iterate on the bottleneck, and validate that behaviour and readability remain intact. This field guide captures the optimisation mindset we expect in RubyDev PRs.
Optimisation pipeline
- Observe: Use application metrics (APM, logs, queue latency) to detect slow hotspots.
- Profile: Run Benchmark::IPS, stackprof, or tracing to isolate the slow section.
- Experiment: Try one change at a time (algorithm, data structure, cache) in a branch.
- Validate: Re-run profiles, ensure test suite/behaviour unchanged, gather confidence.
- Deploy & monitor: Confirm gains remain after deploy using dashboards and alerts.
Database & query tuning tips
- Add covering indexes for frequent WHERE + ORDER BY combinations (`lessons(user_id, published_at DESC)`).
- Use `select` to fetch only required columns before serialising responses.
- Batch updates/inserts with
insert_allor database-native bulk features. - Move computational heavy lifting (aggregations) into SQL when possible.
Concurrency considerations
For CPU-bound work, threads are limited by the GVL; use Ractors or move heavy computation to background jobs. For IO-bound operations (HTTP, database), ensure connection pools are sized appropriately and consider async pipelines (Sidekiq, ActiveJob) to hide latency.
Case study: lesson feed
Our lesson feed initially loaded 50 lessons per request (600ms). After profiling we:
- Converted N+1 queries into a preloaded join.
- Cached author avatars via a low-level Redis cache.
- Paginated results with `pagy` and served 15 lessons per page.
- Added Benchmark::IPS script to guard against regressions.
Result: median response time dropped to 120ms with significantly lower DB load.