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
Is machine learning hard? Not always - The Intercom Blog
The post argues that machine learning does not always require advanced mathematics and, as described by Intercom’s Fergal Reid, is about systems that improve with more training data. It contrasts a naive keyword-matching approach in Ruby for a ‘You might also like’ feature, which produced irrelevant results, with a basic machine learning method using K-Means clustering. The machine learning approach first represents each blog post as a binary vector indicating the presence of each word from the entire corpus. The K-Means algorithm then iteratively assigns each post to the nearest cluster center and recalculates centers as the average of assigned posts until assignments stabilize. The post demonstrates that roughly 40 lines of Ruby code can produce relevant recommendations and encourages readers to try libraries like Scikit Learn.
Welcoming Mateus Resende, our new Software Engineer
Resend introduces Mateus Resende as a Software Engineer based in Belo Horizonte, Brazil. He began working in software in 2015, first as a Ruby and Ruby on Rails developer before moving across domains including e-commerce recommendations, vehicle tracking, fintech, and email systems. A snowstorm during his exchange at SUNY Buffalo led him to discover MIT OpenCourseWare, code for hours, and later switch from Electrical Engineering to Computer Science. At Resend, he says its remote and async culture, extensive documentation, and developer-oriented product and company shaped his decision to join. His approach to engineering emphasizes balancing quality with speed and tailoring solutions to users and reviewers, while he recommends fundamentals such as testing, Domain-Driven Design, and Evolutionary Architectures over relying on hyped LLM coding techniques.
Upgrade to Puma 7 and Unlock the Power of Fair Scheduled Keep-alive
Puma 6 and earlier could let a keep-alive connection “cut the line” when all threads were busy, allowing up to ten requests from that connection to run consecutively while previously accepted work waited. The behavior came from reusing a “fast inline” optimization intended for HTTP pipelining, where requests arrive together, for keep-alive requests that can arrive later. Puma 7 changes queueing so pipelined and keep-alive requests return to the back of the queue, and adjusts wait_until_not_full so interleaving is possible; its accept loop now slows proportionally with load instead of stopping completely. The fix does not add capacity, but distributes overload delay more fairly across requests. The post recommends upgrading to Puma 7.1.0 or later, especially for applications that had disabled keep-alives after latency spikes.
Automated Sentry Error Tracking
Fly.io announces Sentry as the default error-monitoring tool for its applications, with new deployments receiving an automatically provisioned Sentry project and existing apps using flyctl ext sentry create. Each organization receives one year of monthly Team Plan credits covering 50,000 error events, 100,000 performance units, 500 session replays, and 1GB of attachment storage. For Rails apps, flyctl launch installs the required gems, creates initializers, and sets the SENTRY_DSN secret, allowing boot failures, memory exhaustion, exceptions, latency issues, and crashes to reach Sentry. The post demonstrates a deliberately raised Ruby exception, then uses flyctl apps errors to open the Sentry issue dashboard and identify the bug; planned extensions include release tracking and out-of-memory events.
Implementing Equality in Ruby
Ruby has several equality and comparison mechanisms, so custom classes need explicit implementations when instance identity is not the intended meaning. The post distinguishes value objects, whose equality depends on all attributes, from entities, whose equality depends on an explicit identifier; its Point and Employee examples also use class checks, while an entity with a nil ID is unequal to other entities. It defines reflexivity, symmetry, and transitivity as properties to preserve, notes NaN as an exception to reflexivity, and explains #equal?, #eql?, #hash, and #===. For ordered values, #<=> returns -1, 0, 1, or nil, while Comparable supplies relational operators and useful methods including #min, #sort, #between?, and #clamp.
Logbook - 2022-05-13
The May 13, 2022 log records Fly.io changes and fixes from the preceding week, spanning infrastructure capacity, account self-service, API maintenance, documentation, and customer-facing behavior. New servers in syd, iad, and dfw, plus three workers in ord, addressed capacity shortages that had caused provisioning or deployment problems. The API moved to Ruby 3.1, with anticipated performance and security-patch benefits, while an internal Elixir UI guide and clearer app and machine name validation errors aimed to reduce development and debugging friction. Operational work permanently disabled consul-templaterb on fly-proxy nodes and fixed a fly-proxy memory leak, giving those nodes more CPU/RAM headroom; an MTU workaround was intended to improve IPv6 tunnel connectivity, while a larger fix remains. Other fixes covered account-deactivation guidance, personalized support email addresses for paid plans, billing, volume metrics, forum registration, and a minor “X daysago” display error.
The Adventures of Rendezvous in Heroku’s New Architecture
Heroku’s migration of Rendezvous, the bidirectional proxy that connects Heroku Run sessions to one-off dynos, from EC2 Classic to AWS VPCs caused one customer’s app to hit H12 timeouts. The move replaced a single Elastic IP ingress and process-level TLS termination with a Network Load Balancer, whose larger TLS frames exposed a blocking flaw in Rendezvous’s single-threaded Ruby channel loop. That loop used IO.select on the TCP socket and dyno terminal, but OpenSSL::SSL::SSLSocket could retain unread buffered data after a partial read; IO.select saw no TCP bytes and blocked beyond the Heroku Router’s 30-second limit. Engineers reproduced the behavior across two languages, instrumented the instance agent, then fixed the channel with separate threads and corrected socket-reading and IO.select handling. After staging tests, the customer moved back to VPC Rendezvous and confirmed the issue was resolved.
How to Build a Web App with and without Rails Libraries
This tutorial builds Mirth, a Ruby web application that lets users view displayed data and enter new data that persists, using a birthday tracker as its example. It first constructs the application with core Ruby libraries, explaining TCP sockets, HTTP requests and responses, persistent storage, and the need for a web server interface such as Rack. It then replaces that lower-level code with Rails libraries, including Action Controller, Action Dispatch, Active Record, and Action View, while using ERB templates and a configured view path. The tutorial shows how routing, database access, request handling, and HTML generation fit together, with the application available at localhost:1337/birthdays after the Rails-library version is run. Its conclusion is that building from scratch clarifies the implementation details Rails hides, while Rails remains the practical choice for web development.
Remove Circular Dependencies by Using Dependency Injection and the Repository Pattern in Ruby
An internal Ruby pricing gem used by Shopify Core and Storefront Renderer needed consumer-owned data while retaining shared pricing knowledge, creating a circular dependency. The proposed design puts calculation logic and shared domain types in a stateless gem, while each consumer implements a repository contract for retrieving and returning the required data. Constructor injection passes a PricingRepositoryInterface implementation into PricingEngine::Engine, and Sorbet interfaces and function signatures enforce implemented methods and expected return types. Testing separates gem-isolated tests using repository mocks, consumer unit tests for repository behavior, and integration tests confirming the gem works within each consumer. The result, according to the post, is removal of the circular dependency and a typed contract that makes consumers responsible for data access.
Simplify, Batch, and Cache: How We Optimized Server-side Storefront Rendering
The post explains how a new Ruby-based server-side Storefront Renderer reduced the time required to serve Shopify storefront requests. It combines MySQL multi-statement queries, handcrafted SQL, a thin data-mapping layer built from plain old Ruby objects, query book-keeping with eager- and lazy-loading, multiple LRU caching layers, and techniques for reducing memory allocations. For a product page, one database round trip can load the product, variants, images, shop, theme, and related resources; later requests can replay previously observed queries early, while less frequently used data remains lazy-loaded. The resulting renderer serves 75% of requests in under ~45ms, 90% in under ~230ms, and 99% in under ~900ms, with average response time nearly five times faster than the previous implementation.
The State of Ruby Static Typing at Shopify
Shopify’s Ruby monolith receives about 400 commits and 40 deployments daily, spanning 37,000 files, 622,000 methods, and over 2,000,000 calls, making correctness difficult despite rigorous review and 150,000 automated tests. Since 2018, Shopify’s Ruby Infrastructure team has pursued static typing and adopted Sorbet, discussed in a 2020 Shipit! event. The post describes Sorbet’s handling of simple collections such as T::Array[Integer], nested types using T.untyped, and dedicated classes for complex structures, while noting obstacles involving ActiveSupport::Concern, implicit inclusion requirements, metaprogramming, and dynamic GraphQL resolvers. For Rails projects, guidance is to begin with typed: false, generate RBI files using Tapioca or sorbet-rails, then move suitable files toward typed: true based on reuse, errors, collaboration, or churn, checking generated files into the repository.
Static Typing for Ruby
Shopify’s Ruby monolith spans 37,000 files, 622,000 methods, and more than 2,000,000 calls, making fast feedback and stability difficult despite rigorous reviews and 150,000 automated tests. Sorbet was selected after the team evaluated requirements for gradual typing, speed, and support for Ruby and Rails features including metaprogramming, overloading, and class reopening. Its RBI files represent constructs it cannot infer, while per-file sigils allow adoption to progress without blocking development, and SorbetMetrics tracks sigils, typed calls, and method signatures. Shopify treats typing as a product, combining CI enforcement, developer support, surveys, and interviews to guide rollout and measure sentiment. The excerpt reports 80% of monolith files, including tests, at typed: true or higher, with almost half of calls and methods covered and type checking under 15 seconds on developer machines.
Adopting Sorbet at Scale
Shopify describes its adoption of Sorbet static typing across a Ruby monolith containing 37,000 files, 622,000 methods, and more than 2,000,000 calls. At the time of writing, Sorbet ran on every pull request, 80% of files were typed: true or higher, almost half of calls were typed, and half of methods had signatures. To handle Ruby and Rails idioms, the team built RuboCop Sorbet for compatibility rules, Tapioca for gem and DSL RBI generation, and Spoom for programmatic tooling, metrics, and LSP access. A controlled experiment found fewer production NoMethodErrors in files typed: true after typing about 20% of the application, although the results were preliminary and signatures had not yet been added. Shopify planned to reach typed: true across all files, improve Rails support, and continue collaboration around Sorbet and RBS.
Enforcing Modularity in Rails Apps with Packwerk
Large Rails monoliths can develop high coupling, low cohesion, cross-component calls, shared Active Record models, and classes that know too much because Ruby and Rails provide limited boundary enforcement. Shopify created Packwerk, an open-source static analysis tool that groups Ruby files into packages and enforces dependency and privacy boundaries, including controlled public APIs for constants. It reports the violation type and location with actionable next steps, integrates with CI, and can run locally; installation begins by adding the gem and running packwerk init. Because Ruby static analysis is complex, Packwerk ignores constants that are not autoloaded, reducing false positives at the cost of false negatives. The excerpt says it runs in six Shopify Rails applications, with 48 packages and 30 boundary enforcements in the core codebase, while adoption also prompted work on dependency inversion.
The Life-Changing Magic of Tidying Ruby Object Allocations
The post presents object-allocation profiling as a way to find Ruby performance hotspots, including code inside libraries such as Active Record. It uses memory_profiler and derailed_benchmarks to group allocations by gem, file, and source location, then examines whether each allocation can be removed or avoided. A benchmark compares a direct maximum comparison with an array-based version: the array allocation makes the latter 2.04x slower in the reported run. One optimization for cache_version reuses a raw database timestamp string when it is safe, checking its type, format, timezone, and provenance before deleting separators and padding PostgreSQL-truncated values. This change reduces allocation from 743,842 bytes and 6,626 objects to 702,955 bytes and 6,063 objects, and the post reports up to 1.12x faster; combined optimizations reach 1.23x overall.
Under Deconstruction: The State of Shopify’s Monolith
Shopify describes the ongoing effort to modularize its core Ruby on Rails monolith, which contains more than 2.8 million lines of Ruby and 500,000 commits. The initiative organizes code into independently owned components intended to narrow developer focus, reduce affected test suites, preserve contracts, and clarify operational ownership, while recognizing that large-scale refactoring is also a people problem. The work has already improved exception triage, distributed codebase chores, and design awareness, while its current practices emphasize grassroots participation, tooling, targeted reviews, and a holistic architectural view. Packwerk restricts dependencies for about a third of the 37 main-monolith components. Shopify is pursuing a cleaner dependency graph, componentized Rails applications by default, and isolated component tests, while reserving service extraction for cases such as storefront rendering and credit-card vaulting.
How Shopify Reduced Storefront Response Times with a Rewrite
Shopify rewrote the server-side Storefront Renderer, which loads Liquid themes and storefront data before returning HTML, because the legacy Rails-monolith implementation had developed stricter performance demands and rising time-to-first-byte as traffic grew. The new single-purpose application separates storefront traffic from checkout, admin, and API traffic, uses active-active replication with dedicated read replicas, and adds mechanisms for high-load resilience. During migration, a Ruby verifier compares status codes, headers, and bodies from both implementations, while a custom Lua module on OpenResty samples production traffic and routes requests based on verification results. The rollout had reached more than 90% feature parity, and the new implementation averaged 4x faster server response times, with ongoing work aimed at full parity and retiring the legacy system.
How We Built Size.link
Size.link is a free Shopify AR tool that lets shoppers view a product’s dimensions in their surroundings through a smartphone camera. It was created during April Hack Days as a quick alternative to commissioning 3D models, with requirements including browser-based operation, speed, accuracy, and iOS and Android support. Because iOS lacks JavaScript WebAR, the implementation uses AR Quick Look with USDZ files and Android’s Scene Viewer with GLB files. Rather than generate files dynamically with slow USDZ tooling or pre-generate billions of dimension combinations, the team modifies binary template data, replacing scale or vertex float values to resize the cube while preserving its outline. The resulting Ruby server generates USDZ files in well under one millisecond, and the same strategy produces GLB files; future work could add textures for standard-sized products.
Media at Scale: Callbacks vs pipelines
Shopify’s Rails monolith needed to add native product videos and 3D models while continuing to support an image infrastructure containing more than 7 billion images. The design question was whether media creation should rely on Active Record callbacks or an explicit pipeline, with transactions protecting interdependent database writes. Callbacks were quick for simple cases, but adding media-specific behavior such as video thumbnails spread conditionals across models and made lifecycle ordering difficult to follow and debug. The pipeline design routes requests through a single Product Create Media Service and media handlers, each organized into before_transaction, during_transaction, and after_transaction steps, while confining logic to one media type. This structure separates concerns, controls creation order, limits model access, and makes implementation details easier to understand and maintain as the feature grows.
How We Added Client Rate Throttling to the Platform API Gem
The platform-api gem needed client-side rate throttling for Heroku’s API, which uses GCRA and allows a bucket of up to 4,500 request tokens, replenished at roughly 75 per minute. The goal was to avoid 429 responses without creating retry traffic, while operating within the client library and without coordination across machines. The work combined tests with a simulated server, processes and threads, charting, and metrics for average retry rate, maximum sleep, request-count variance, and workload-clearing time. The winning strategy adjusted sleep downward using the remaining request count relative to the 4,500-request maximum; in simulation it achieved a 3.07% retry rate, 17.32-second maximum sleep, 78.44 standard-deviation request count, and 84.23 seconds to clear 4,500 requests. The change shipped behind a null strategy before becoming the default in version 3.0.0+, with feedback reporting no significant breaks or severe application dysfunction.
Related topics
All topics →Weekly digest
The best engineering reads, once a week.