---
title: "Performance"
description: "817 posts about Performance, summarised, each linking to the original."
---

# Performance
> 817 posts about Performance, summarised, each linking to the original.

## Articles

### [Evolving Alongside your Tech Stack](https://yomu.fyi/post/evolving-alongside-your-tech-stack.md)
- Company: [Heroku](https://yomu.fyi/company/heroku.md)
- Author: Chris Castle
- Published: Apr 30, 2020
- Type: Explainer

Rapid advances in software have expanded both what applications can do and what developers must understand, from efficient code to integration, security, and user experience. As tools make polished interfaces easier to build, engineers are expected to understand users, design sensibilities, system behavior, and constraints such as memory management and CPU load. The post recommends maintaining broad awareness of changing technologies while prioritizing learning around problems actually encountered; Postgres performance issues may warrant understanding traffic and write effects without exhaustive tuning. It cautions against adopting every new framework or database on reputation alone, using MemSQL as an example, and concludes that developers should evaluate trade-offs carefully, build expertise in the tools they operate, and focus on shipping meaningful product work.


### [Optimizing Ruby Lazy Initialization in TruffleRuby with Deoptimization](https://yomu.fyi/post/optimizing-ruby-lazy-initialization-in-truffleruby-with-deoptimization.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Mar 31, 2020
- Type: Explainer

The post examines how TruffleRuby can optimize Ruby’s ||= operator when it is used for lazy initialization rather than repeated assignment. Static profiling of 20 popular open-source projects found 2,082 uses, with 64% meeting conservative criteria based on constant values or naming patterns for parameterless methods assigning instance or class variables. The implementation replaces the usual OrNode with an OrLazyValueDefinedNode, which counts executions of the right-hand side and deoptimizes when it is executed fewer than twice, allowing uncommon assignment paths to remain in the interpreter. In a benchmark, the change compiled code about 6% faster and produced about 63% less machine code by memory, although the post notes that benchmarking larger projects is noisy and the runtime impact is difficult to prove.


### [Tackling UI Test Execution Time Imbalance for Xcode Parallel Testing](https://yomu.fyi/post/tackling-ui-test-execution-time-imbalance-for-xcode-parallel-testing.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Ngoc Thuyen Trinh
- Published: Mar 16, 2020
- Type: Problem & solution

Parallel test execution in Xcode can suffer from test time imbalance when tasks finish at significantly different times across parallel simulator workers. Analysis of Xcode scheduling logs shows that the runner groups tests by test class and dispatches all tests from the same class to a single simulator. Attempts to customize the suite by swizzling XCTestSuite fail because made-up suites initialize only after tests are dispatched. To overcome this grouping constraint, unique tokens or test names are appended to the class name component in \`-only-testing\` command-line arguments. This trick forces Xcode to treat each test as an independent class, successfully distributing individual tests across separate workers.


### [Returning 575 Terabytes of Storage Space to Our Users](https://yomu.fyi/post/returning-575-terabytes-of-storage-space-to-our-users.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Lucas Nelaupe
- Published: Feb 25, 2020
- Type: Problem & solution

Android Vitals data revealed that 15.7% of Grab users had less than 1GB of free device storage and uninstalled the app at 1.2 times the normal rate. To understand on-device storage consumption, the team instrumented session launches using the Android StorageManager API to collect binary size, cache folder size, and total footprint metrics. Analysis showed unusually large cache sizes driven by orphaned cache folders from discontinued third-party libraries, including an image library replacement of Picasso by Glide. An automated cleanup routine deployed in app updates purged legacy cache directories upon launch. This mechanism reclaimed 575 terabytes of junk data across more than 13 million devices, averaging 40MB per user.


### [Turboku - The Art Of Faster Heroku Apps](https://yomu.fyi/post/turboku-the-art-of-faster-heroku-apps.md)
- Company: [Fly.io](https://yomu.fyi/company/fly-io.md)
- Published: Feb 5, 2020
- Type: Benchmark

Turboku is a migration path that automatically turns Heroku web dynos into Fly applications, addressing how existing Heroku apps can move to Fly’s globally distributed edge network. Fly routes requests to the closest datacenter through one global IP address, or two with IPv6, while TLS terminates closer to users and modern ECDSA certificates are automatic for Fly domains. A benchmark around the world used an existing application to compare Heroku and Fly connection stages, full requests, and cached requests. Fly reduced DNS, TCP, TLS, network, and full-request times, with the largest reported improvement appearing when caching was used: 180.16 ms versus Heroku’s 817.75 ms, a 77.91% reduction; uncached full requests fell 35.39%, while TTFB was 1.24% higher.


### [How We Prevented App Performance Degradation from Sudden Ride Demand Spikes](https://yomu.fyi/post/how-we-prevented-app-performance-degradation-from-sudden-ride-demand-s.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Corey Scott
- Published: Jan 8, 2020
- Type: Problem & solution

Grab experienced severe system strain when sudden localized spikes in ride demand, triggered by events like heavy rain or concert dismissals, coincided with driver shortages. These localized bursts overloaded the platform and degraded the experience for users outside the affected areas. To mitigate this, engineers created the Spampede filter, a circuit-breaker mechanism placed at the start of the booking pipeline. The filter converts pickup locations into Geohash Integer buckets and partitions time using Unix timestamps, tracking unfulfilled requests in Redis with atomic increments and time-to-live expirations. When unallocated requests exceed configured thresholds within a specific bucket, the system immediately short-circuits new incoming bookings to protect overall platform stability.


### [Journey to a Faster Everyday Superapp Where Every Millisecond Counts](https://yomu.fyi/post/journey-to-a-faster-everyday-superapp-where-every-millisecond-counts.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Renu Yadav
- Published: Dec 26, 2019
- Type: Problem & solution

Grab undertook an initiative to reduce startup time and improve time to interactive (TTI) on its passenger mobile app. Because local benchmarks failed to simulate real device and network conditions, the team instrumented code in production across 8–9 million daily users to capture p50 and p95 metrics. Initial gains came from caching service tiles between sessions and removing a startup animation, saving four seconds. Architectural changes followed, including converting iOS dynamic frameworks to static linking and merging others, while Android initialisation was refactored with Kotlin coroutines. Replacing a heavy third-party analytics library with an internal experimentation platform yielded further startup reductions.


### [Slow Database Queries? Are Database Types to Blame?](https://yomu.fyi/post/slow-database-queries-are-database-types-to-blame.md)
- Company: [Heroku](https://yomu.fyi/company/heroku.md)
- Author: Ben Fritsch
- Published: Dec 19, 2019
- Type: Problem & solution

Slow database queries can stem from schema choices as well as inefficient SQL. The example uses PostgreSQL with a json column holding large payloads, then selects a single url value for an app\_uuid and orders the results. EXPLAIN ANALYZE with BUFFERS showed 7,106 shared buffer hits, representing about 60MB of JSON data loaded and discarded, while execution reached 1,141.296 milliseconds in the reported run. The solution split the JSON into separate text fields for data and url, bringing query time down from about 1,200 milliseconds to 10 milliseconds. The broader conclusion is that schemas should be revisited as application usage becomes better understood, but changes are not necessary when the design isn't causing a problem.


### [Driving Southeast Asia Forward Through People-Focused Design](https://yomu.fyi/post/driving-southeast-asia-forward-through-people-focused-design.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Philip Madeley
- Published: Nov 5, 2019
- Type: Explainer

Designing digital products for Southeast Asia requires tailoring user experiences to unique regional constraints and consumer behaviors across diverse populations. Users in the region often operate low-end mobile hardware on congested networks while carefully rationing prepaid mobile data. Grab addresses these challenges by designing comprehensively for non-ideal UI stacks, implementing loading skeletons, and replacing heavy video tutorials with lightweight SVG animations to minimize bandwidth consumption. Furthermore, product teams adapt to mobile-only environments by prioritizing phone number and one-time-password registrations while avoiding legacy desktop-era iconography. Visual accessibility is validated by testing UI readability on dimmed, low-resolution screens under bright ambient sunlight.


### [Sam Saffron AMA: Performance and Monitoring with Ruby](https://yomu.fyi/post/sam-saffron-ama-performance-and-monitoring-with-ruby.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Nov 5, 2019
- Type: Explainer

Sam Saffron discusses Discourse’s approach to Ruby performance and monitoring, arguing that production memory constraints matter more than isolated microbenchmarks for many deployments. He favors clear code and selective optimization, sometimes bypassing ActiveRecord with MiniSql on performance-sensitive paths rather than broadly sacrificing Ruby’s readability. Discourse keeps performance under continuous observation, and Saffron describes budgets for dependencies, boot time, and high-profile pages, including alerts for query-count regressions. He identifies memory leaks as especially difficult to diagnose, describes bisecting an application to isolate a V8-Ruby interop leak, and says MRI remains the only feasible runtime for Discourse while memory profiling and analysis tooling remain substantially behind Java and .NET.


### [Griffin, an Anti-fraud Risk Rule Engine Making Billions of Predictions Daily](https://yomu.fyi/post/griffin-an-anti-fraud-risk-rule-engine-making-billions-of-predictions.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Muqi Li
- Published: Oct 28, 2019
- Type: Problem & solution

Grab's Trust/Identity/Safety team built Griffin, an in-house anti-fraud risk rule engine designed to process billions of daily predictions across multiple business verticals. Initially, Grab managed fraud rules directly within backend service code, but escalating rule complexity, tight rule interdependencies, and translation gaps between data scientists and developers caused deployment delays and misfiring errors. To overcome the limitations and steep learning curves of third-party engines like Drools, the team separated the workflow into data orchestration and rule-based prediction. Griffin enables analysts and data scientists to author Python-based rules directly via a web portal and reload updated logic into memory without manual developer intervention. By eliminating I/O during rule evaluation and leveraging Gunicorn multi-processing, the engine handles over 100,000 queries per second at peak on six EC2 instances with single-prediction latencies under six milliseconds.


### [Using Grab’s Trust Counter Service to Detect Fraud Successfully](https://yomu.fyi/post/using-grab-s-trust-counter-service-to-detect-fraud-successfully.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Chao Wang
- Published: Oct 21, 2019
- Type: Problem & solution

Grab's Trust Platform team built the Counter service to detect fraud across business verticals like transportation, food, and payments. The platform replaces manual, multi-week engineering cycles with a self-service UI where data analysts can define and experiment with counters independently. Operating on an asynchronous ingestion and synchronous transaction model, the architecture evaluates incoming stream data, enriches it via internal services, and persists aggregated signals to ScyllaDB through Grab-Stats. A multi-bucket strategy partitions queries into fifteen-minute, hourly, and daily granularities to maintain low-latency query aggregations across wide time ranges under strict SLAs.


### [How to Write Fast Code in Ruby on Rails](https://yomu.fyi/post/how-to-write-fast-code-in-ruby-on-rails.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Oct 8, 2019
- Type: Tutorial

Shopify’s guide presents performance advice for Ruby on Rails across Active Record, Rails, and Ruby, while treating speed as a feature rather than the first optimization priority. It recommends understanding Active Record’s lazy query execution, selecting fewer columns, avoiding unindexed queries, using safe indexing approaches for large tables, and treating query cache as short-lived rather than dependable. For Rails applications, it covers caching, throttling expensive or abusive operations, moving long-running work into Active Job-backed queues, and reducing dependency growth to limit boot time and memory use. Ruby-specific guidance includes limiting metaprogramming and indirection, choosing O(1) hash lookups over O(n) array searches when appropriate, and reducing allocations while avoiding harmful global mutation; benchmark figures show method-definition and invocation costs can differ.


### [Want to Improve UI Performance? Start by Understanding Your User](https://yomu.fyi/post/want-to-improve-ui-performance-start-by-understanding-your-user.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Sep 20, 2019
- Type: Tutorial

Shopify’s team examined UI performance in the Marketing section of the Shopify admin, aiming to improve load time, perceived load time, and interactions after navigation. They tested the experience on a low-end Moto G3, profiled browser activity with Chrome Developer Tools and React DevTools, and used merchant workflows to reorder priorities. The work reduced unnecessary initial work by memoizing repeated date-formatting tasks, deferring the nonessential ImagePicker bundle, and prefetching that component and its data when a merchant hovered over its activator; memoization cut one component’s render time from about 64.7 ms to 0.5 ms. For loading states, the team recommends stable skeletons with static headings and immediately useful controls, such as the Create campaign button, so merchants can act before unrelated data arrives.


### [Pagination with Relative Cursors](https://yomu.fyi/post/pagination-with-relative-cursors.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: 2023-10-18
- Published: Aug 12, 2019
- Type: Explainer

Incremental page-number pagination becomes increasingly expensive for large offsets, because the database scans and discards earlier records before returning a page. Shopify describes relative cursor pagination, which remembers the last record seen and continues from that point, using an indexed id to keep query time nearly constant as pages increase. In tests on a shop with 14 million products, a 100,000 offset took 2,221.60 ms, compared with 5.24 ms using the last id, while offsets of 1,000,000 and above consistently timed out. Sorting by another field requires retaining both its last value and a unique secondary key such as id, with a matching compound index to avoid skipped records and maintain performance. Shopify introduced Link-header URLs for next and previous pages and began migrating REST endpoints from page numbers, reporting early /admin/products.json usage as about 11 times faster, though adoption remained low and full results were pending.


### [How We Built a Logging Stack at Grab](https://yomu.fyi/post/how-we-built-a-logging-stack-at-grab.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Daniel Kasen
- Published: Jul 31, 2019
- Type: Problem & solution

Grab needed a scalable logging platform to replace slow, fragmented systems that hindered debugging across their growing service fleet. Generating 25TB of daily logs, the team built a horizontally scalable Elasticsearch cluster configured via Ansible and monitored with Datadog. Although the initial proof of concept assigned all node roles (ingest, coordinator, master, and data) to every machine, operating at scale introduced major challenges with JVM heap exhaustion and cluster stability. The team resolved memory pressure and performance bottlenecks by tuning circuit breakers, lowering field data cache limits, adjusting shard allocations based on segment memory, and disabling translog compression during shard transfers.


### [Recipe for Building a Widget: How We Helped to “Peak-Shift” Demand by Helping Passengers Understand Travel Trends](https://yomu.fyi/post/recipe-for-building-a-widget-how-we-helped-to-peak-shift-demand-by-hel.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Lara PuReum Yim
- Published: Mar 7, 2019
- Type: Problem & solution

Transport demand spikes during regular commuting hours often outpace driver availability, resulting in passenger wait times and fare surges. To mitigate these imbalances, Grab created the Travel Trends Widget for its mobile feed to redistribute ride requests toward off-peak windows. The widget uses machine learning forecasting to present historical supply-demand patterns alongside pricing trends for the upcoming two hours. To handle anticipated high query rates across millions of database entries, engineers periodically load precomputed trend data into an in-memory data structure rather than querying the database per request. The feature rolled out to feeds in Singapore and Jakarta within four weeks of initial development.


### [A Lean and Scalable Data Pipeline to Capture Large Scale Events and Support Experimentation Platform](https://yomu.fyi/post/a-lean-and-scalable-data-pipeline-to-capture-large-scale-events-and-su.md)
- Company: [Grab](https://yomu.fyi/company/grab.md)
- Author: Oscar Cassetti
- Published: Jan 16, 2019
- Type: Problem & solution

Controlled online experimentation across diverse product verticals requires tracking interactions across systems to prevent local optimizations from causing global degradation. Grab built a batch data pipeline to capture, ingest, and process petabytes of event data to support its experimentation platform and analytics stakeholders. The architecture loads ingested event data from Amazon S3, transforms and sorts it, and writes partitioned output back to S3 with metadata registered in Apache Hive. Using Apache Spark on AWS Elastic MapReduce with Apache Airflow for orchestration, the system handles roughly 400,000 incoming events per second. The data is partitioned by event type and ingestion time and stored in Apache ORC format to streamline query workloads and reduce retrieval overhead.


### [Ruby 2.6 Released: Just-In-Time Compilation Is Here](https://yomu.fyi/post/ruby-2-6-released-just-in-time-compilation-is-here.md)
- Company: [Heroku](https://yomu.fyi/company/heroku.md)
- Author: Jonan Scheffler
- Published: Dec 26, 2018
- Type: Announcement

Ruby 2.6 introduces an initial Just-In-Time (JIT) compiler, making the feature available through the --jit option and on Heroku. The implementation follows Takashi Kokubun’s YARV-MJIT approach, retaining Ruby’s existing YARV instructions instead of replacing them with the proposed RTL intermediate representation; it starts a compiler process at startup and supports GCC, Clang, and Microsoft Visual C++. On Heroku, applications can select Ruby 2.6 in the Gemfile and enable JIT through a Procfile flag or RUBYOPT, although setting RUBYOPT with heroku config is not yet supported. The optcarrot benchmark showed a reported 1.8x speed increase over Ruby 2.5, while Rails and Sidekiq were slower because compilation costs and roughly 2MB per compiled method create overhead. JIT compaction reduces memory use near the 1,000-method cache limit, but the source says further work is needed before JIT becomes the default.


### [How an Intern Released 3 Terabytes Worth of Storage Before BFCM](https://yomu.fyi/post/how-an-intern-released-3-terabytes-worth-of-storage-before-bfcm.md)
- Company: [Shopify](https://yomu.fyi/company/shopify.md)
- Author: Gurpreet Gill
- Published: Dec 14, 2018
- Type: Problem & solution

While preparing for Black Friday Cyber Monday (BFCM), Shopify’s Products team found that historical reporting records were causing MySQL to scan many InnoDB pages to return a single record. An intern built a Rails background job to delete the records safely, using Shopify’s job-iteration library to process small batches and an internal throttling enumerator to pause when database health or replication lag became a concern. Indexed fetch queries and cursor-based batching avoided repeated scans and timeouts, while the iterator deleted each batch. Run about two weeks before BFCM, the job deleted more than 5 billion records, peaked at approximately six million records per minute, and released three terabytes of storage. Afterward, the database scanned around three times fewer pages per returned record, improving resilience to flash-sale traffic and producing large cost savings.


[Newer posts](https://yomu.fyi/topic/performance/page/39.md) · [Older posts](https://yomu.fyi/topic/performance/page/41.md)
