Loading…
Topic
Ruby
52 posts about Ruby. Every summary links to the original.
- 52 posts
- 5 companies
The Good Stuff
Paid picks we use ourselves. Links carry a referral code — a subscription through one earns us a commission at no extra cost to you.
Newest first
Posts
Writing Better, Type-safe Code with Sorbet
Ruby repositories can hide unsafe method calls and ambiguous data shapes when types are inferred from variable names alone. Sorbet static type checking addresses these risks with method signatures, typed structs, enums, and interfaces. It can flag a potentially nil return before a chained call, distinguish database output fields from input fields, and enforce contracts for synchronous and asynchronous indexers in a dependency-injected hexagonal structure. The article also describes Sorbet’s gradual typing, including five strictness levels and namespace-by-namespace adoption using the minimum typed level of true. The stated conclusion is that enforced type safety catches errors unit tests may miss and helps prevent unsafe code from reaching production.
Spark Joy by Running Fewer Tests
Shopify’s monolithic Ruby repository has more than 150,000 tests, grows 20–30% annually, and takes 30–40 minutes to run across hundreds of Docker containers, making required full-suite CI costly and vulnerable to intermittent failures. The team built a dynamic-analysis test-selection system that logs method calls during each test, records files in each call graph, and maps changed files to relevant tests. Because Ruby, Rails metaprogramming, and non-Ruby files complicate tracing, the system adds Rails patches and fallback rules, runs extra tests when mappings lag, and executes the full suite asynchronously on every deploy. After two months and 8,360 merged commits, it achieved 99.94% failure recall, selected about 60% of tests, and reduced compute time by roughly 25%. Developers requested full-suite runs on fewer than 2% of pull requests.
Understanding Programs Using Graphs
TruffleRuby uses a sea-of-nodes graph as an intermediate representation after parsing, allowing its just-in-time compiler to optimize Ruby programs and translate them to machine code. The explanation contrasts this graph with an abstract syntax tree, then shows how control flow, data flow, side effects, pure computations, loops, and phi nodes are represented through boxes and arrows. A three-way conditional demonstrates global value numbering: a repeated multiplication becomes one movable computation that can float across branches without changing program behavior. A loop example shows backward control flow and repeated functional computation, while the discussion weighs graph-based optimization against poor compactness and readability at larger scales. Shopify is building graph-drawing and Ruby decompilation tools to inspect optimization at codebase scale.
Dev Degree: Behind the Scenes
Dev Degree is Shopify’s work-integrated learning program, designed to combine an accredited computer science degree with four years of continuous developer experience. Students take three university courses per semester while working 25 hours weekly, beginning with Shopify-led skills training before moving through four team placements across disciplines such as back-end, front-end, data, security, and production engineering. The program teaches tools and technologies including Git and GitHub, Ruby, Rails, React, TypeScript, and GraphQL, while emphasizing mentorship, feedback, personal development, and support from a multidisciplinary team working with university partners. The first cohort graduated on April 24, 2020, after adapting to program changes and remote learning during the pandemic; the post reports that 100% of graduates accepted full-time positions within six months.
How to Fix Slow Code in Ruby
Performance regressions can accumulate in a large monolithic Rails application, making it difficult to identify offending changes among thousands of daily commits. The post presents profiling and benchmarking as complementary practices: profiling locates runtime bottlenecks, while benchmarking compares code paths and validates fixes. It covers elapsed time, CPU versus wall time, object allocations, TracePoint and ObjectSpace, plus rbspy, stackprof, rack-mini-profiler, and App Profiler, which supports on-demand remote production profiling at Shopify. A flamegraph example showed garbage collection consuming about 35% of CPU time in a slow request, and the team inferred excessive Ruby object allocation; a Rails benchmark showed roughly 50x improvement from caching an order’s total price calculation. The discussion cautions against micro-optimizations whose gains do not justify code changes and recommends addressing larger performance issues first.
Optimizing Ruby Lazy Initialization in TruffleRuby with Deoptimization
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.
The Curious Case of the Table-Locking UPDATE Query
The post recounts a PostgreSQL performance incident in which metadata processing stopped as jobs accumulated and an UPDATE targeting a single UUID remained active for days. Diagnostics showed the query consuming more than 80% of database time, while manual execution did not reproduce the delay. A pg_stat_activity and pg_locks query exposed many waiting statements and an AccessExclusiveLock entry whose locktype was tuple, leading the investigation toward PostgreSQL row locking and contention. The eventual cause was threaded code that issued concurrent updates to the same row for each job; removing that concurrency, aborting old backends with pg_cancel_backend, and deploying the change restored processing and began churning through the backlog. A correction notes that the reported lock was on a tuple rather than the table, and that PostgreSQL does not have lock promotion as suggested in the debugging discussion.
Sam Saffron AMA: Performance and Monitoring with Ruby
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.
Static Typing in Ruby with a Side of Sorbet
An experiment introduced Sorbet into a greenfield Ruby codebase with four developers to test whether static typing could catch bugs earlier, ease refactoring, and improve code design. Sorbet combines static linting with runtime checks that verify method parameters and return values against signatures, while RBI files describe methods from gems and Rails models. Setup required sorbet init, running sorbet tc, and adding that command to CI; however, Rails metaprogramming required model signatures, and sorbet-rails could miss or mistype fields. Sorbet immediately exposed issues including repeated method calls, unintended nullable database columns, and unnecessary explicit returns or guard clauses. The team reports a very positive short-term experience, while noting that the project’s young age caused most of the bugs encountered and that long-term value remains uncertain.
How to Write Fast Code in Ruby on Rails
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.
Living on the Edge of Rails
Shopify describes running its Core monolith on the unreleased Rails 6 master branch, or “Edge of Rails,” since February 2019, with the goal of continuously using the framework’s latest revision. Instead of annual upgrades, weekly changes reduce diff size and let developers use upstream features quickly, while a 130,000-plus-test suite finds edge cases and informs patches to Rails and related projects such as Zeitwerk. Solid Track automates weekly Rails bumps, opens GitHub pull requests, and triggers git bisect when CI fails to identify the breaking commit. Green builds move through ten-minute canary deployments before reaching the remaining servers. The approach trades the stability of released dependencies for faster fixes and features, relying on monitoring, tests, and controlled deployment to manage risk.
Puma 4: Hammering Out H13s—A Debugging Story
Heroku investigated mysterious H13s—“Connection closed” without response—affecting Ruby applications during dyno deployments or scaling, especially at high traffic levels. The failure involved a race between SIGTERM shutting down Puma and the router’s separate system continuing to deliver a few requests, but comparison with other webservers indicated Puma needed to handle the condition. A reproduction used Rack applications that sent SIGTERM to Puma and themselves, plus parallel curl requests, reproducing curl exit code 52 and Heroku H13s. Debugging showed Puma relied on process termination to close accepted sockets; Puma 4 instead closes binder listeners before shutdown, allowing the router to retry connections while preserving responses for in-flight requests.
Saved by the Schema: Using JSON Schema to Document, Test, and Debug APIs
Heroku uses JSON Schema to define Platform API response structures, generate documentation, and validate endpoint behavior. A CI-invoked rake task detects schema changes and uses prmd to generate Markdown API reference material, while committee tests API responses with assert_schema_conform. Those tests exposed an edge case in Enterprise usage data: dyno values with many decimal places were parsed by Oj as BigDecimal and serialized as quoted strings, whereas integer and ordinary float values remained numbers. Heroku traced this behavior through MultiJson and configured Oj with bigdecimal_load: :float, because the endpoint had sufficient precision at 15 decimal places and its schema required a number. The conforming test then passed, confirming that the schema and generated documentation matched returned API values.
Seven Ways to Fortify Your Application
Applications can fail when a single upstream service goes down, prompting tools that shed load, fail gracefully, and protect struggling dependencies. The discussion frames this as application resilience for everyday failures, distinct from request-level retries, rollback strategies, and catastrophic disaster recovery. It presents maintenance mode as an easily toggled hard stop with a clear message and status-page link, read-only mode as a way to preserve access when modifications fail, and feature flags as controls for global or meaningful groups. Implementation examples include environment variables, relational databases, Redis, in-memory caches, and an S3-based check, with state stored primarily in Postgres and Redis at Heroku. The closing guidance says maintenance mode should be easy to toggle, while read-only mode can preserve user confidence and access to information.
Debugging in Ruby—Busting a Year-old Bug in Sprockets
Sprockets, the Ruby asset packaging system used in Rails’ asset-processing pipeline, had a bug that made assets:precompile fail after a project directory was renamed between runs. The debugging walkthrough reproduces the failure with CodeTriage by clearing caches, compiling assets, moving the project, and compiling again, yielding an error for an outdated absolute path. It then uses bundle open, source inspection, recursive grep, and IRB to examine Sprockets’ dependency cache; the IRB cache lookup does not work because the cache key differs from the cache ID, but the on-disk record exposes an absolute asset path where a relative path is expected. The proposed fix updates loader.rb’s to_load and to_link handling with compress_from_root, and the pull request adds a regression test.
Building a Service-oriented Architecture with Rails and Kafka
Service-oriented Rails applications can use Kafka to move high-volume events between producers and consumers without coupling downstream availability to an upstream request. Kafka persists events for a configured period, lets consumers manage offsets, and distributes replicated data across broker partitions, while ordering is guaranteed within a partition rather than across a topic. The post shows a Rails setup using the ruby-kafka gem, an asynchronous producer, JSON payloads, topics, and partition keys, then applies the event stream to order creation, email, and fulfillment workflows. It also addresses operational concerns including consumer scaling, lag metrics, duplicate delivery, idempotent processing with UPSERTs or event identifiers, and schema evolution, contrasting JSON’s accessibility with Avro’s stronger schema support.
Ruby 2.6 Released: Just-In-Time Compilation Is Here
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.
Introducing the Deprecation Toolkit
Shopify’s Deprecation Toolkit is an open-source Ruby gem for tracking deprecated code in an application and preventing new deprecations from entering a codebase. It addresses the limits of warning messages and the impracticality of immediately raising on every existing deprecation, especially in a large monolith with complex dependency upgrades. The toolkit records deprecations triggered by test suites in YAML whitelist files, then compares later test runs with the accepted set; mismatches trigger configurable behavior, raising an error by default. Existing deprecations can therefore remain acceptable while teams fix them gradually, whereas introduced or removed deprecations are surfaced in CI. Shopify reports that using the toolkit reduced deprecations, supported dependency upgrades, blocked pull requests introducing new ones, and enabled team-based tracking of remediation progress.
Shaping the Future of Payments in the Browser
Shopify tested whether the Payment Request API was ready for its merchants and buyers on supported browsers. The team defined checkout completion rate, time to purchase, drop-off, and qualitative checkout experience as success measures, then ran an A/B experiment with selected merchants and eligible buyers. The isolated Ruby app injected storefront JavaScript, checked eligibility and credit-card support, and routed buyers to either the regular checkout or a browser-provided payment sheet. The experiment recorded a 7% checkout conversion rate drop and a 60% drop at the payment sheet, although time to completion medians were promising. Shopify concluded Payment Request was not yet a good fit, while expecting greater familiarity, payment-sheet improvements, and third-party payment methods through Payment Handler API to help adoption.
Behind The Code: Jennice Colaco, Backend Developer
Jennice Colaco is a backend developer who has worked on Shopify’s Marketing Technology team for 10 months. She holds a Bachelor of Applied Science in Computer Engineering from the University of Toronto, and describes her team’s work as building tools that help merchants market products to customers. She treats programming languages as problem-solving tools, is improving her Ruby skills, and uses byebug to step through code line by line when debugging. Her workday combines early planning, coding, pull-request reviews, pairing sessions, and Slack collaboration, while difficult problems are approached through reading, research, whiteboarding, and asking peers for support. She is taking an in-house Ruby course and describes Shopify’s culture as supportive of individual growth, with ongoing conversations about healthy work environments.
Related topics
All topics →Weekly digest
The best engineering reads, once a week.