Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Millions on Rails: a two-decade retrospective o...

Millions on Rails: a two-decade retrospective on scaling with Rails

Slide links:

* Slide 10: https://x.com/yatish_me/status/1977521025983324491
* Slide 11: https://byroot.github.io/opensource/ruby/2025/10/09/dear-rubyists.html
* Slide 14: https://highscalability.com/scaling-twitter-making-twitter-10000-percent-faster/
* Slide 26: https://lessonsofacto.com/videos/022-yagri-you-aint-gonna-regret-it/
* Slide 32: https://lessonsofacto.com/videos/009-offload-over-95-percent-of-your-origin-traffic-to-your-cdn/
* Slide 33: http://youtu.be/QZo6L6IZm2o
* Slide 37/38: https://judoscale.com/blog/planning-sidekiq-queues
* Slide 40: https://mashable.com/article/cloudflare-accidentally-ddos-attacked-itself
* Slide 41: https://api.rubyonrails.org/classes/ActionController/RateLimiting/ClassMethods.html
* Slide 46: https://youtu.be/pOyvSxB9qFQ
* Slide 51: https://github.com/typhoeus/typhoeus
* Slide 54: https://12factor.net/
* Slide 57: https://github.com/zombocom/rack-timeout
* Slide 59: https://github.com/typhoeus/typhoeus
* Slide 60: https://github.com/shiftcommerce/shift-circuit-breaker
* Slide 69: https://radicalsimpli.city
* Slide 70: https://httparchive.org/reports/techreport/tech
* Slide 71: https://youtu.be/f5felHJiACE
* Slide 72-73: https://lorenstew.art/blog/progressive-complexity-manifesto
* Slide 76: https://lessonsofacto.com/videos/007-sweat-your-tech-stack/
* Slide 78-80: https://developer.chrome.com/docs/web-platform/implementing-speculation-rules
* Slide 82-84: https://developer.chrome.com/docs/web-platform/view-transitions
* Slide 87: https://web.dev/articles/css-scroll-snap & https://developer.chrome.com/blog/carousels-with-css
* Slide 88: https://developer.chrome.com/docs/css-ui/scroll-driven-animations
* Slide 89: https://developer.chrome.com/blog/css-scroll-state-queries
* Slide 91: https://youtu.be/QW6GECIzvsw
* Slide 93: https://youtu.be/cCOL7MC4Pl0 & https://rumvision.com/
* Slide 94: https://lessonsofacto.com/

Avatar for Ryan Townsend

Ryan Townsend

October 16, 2025
Tweet

More Decks by Ryan Townsend

Other Decks in Programming

Transcript

  1. • Leeds-based • >20 years in web development, primarily eCommerce

    & SaaS • Picked up Ruby on Rails at v0.9 • 10 years as CTO • Co-founded SH/FT Commerce • Spoken at >20 conferences globally • Lifter, Foodie, Coffee, Wine, Travel
  2. • Responsible for over £200m in yearly client revenue •

    Load-tested to handle over £1bn • SurvivedThrived through successive Black Fridays • Grew without signi fi cant rewrite • Saved ~13,000 jobs during COVID lockdown
  3. Permanent Abandonment Rate 0% 10% 20% 30% 40% Unavailable Slow

    28% 9% Source: Akamai, The Impact of Web Performance on E - Retail Success
  4. “Every big organization runs into scaling issues, and the simplest

    story is that the tech stack is to blame. [...] It does take leadership to not accept the easy scapegoat.” - Trevor John, thirdhalf.io “Ruby and Rails remain the default stack at Shopify, and the only reason for that is the CEO.” - Jean Boussier (aka byroot), Rails Core Team Source: byroot.github.io/opensource/ruby/2025/10/09/dear-rubyists.html
  5. “Rails gives you the power to get to scaling problems.

    [...] Most people can't even get [to] that.” - Jack Sharkey, Whop @ Rails World 2025
  6. • Database denormalisation • Microservices • Kubernetes • to 'Dockerise'

    everything • NoSQL • GraphQL • Infrastructure as code • Geographical replication • Event streams • Serverless functions • Multi-master databases • Client-generated UUIDs • Single Page Apps (SPAs) • Actual real-time * IF YOU NEED THEM: YOU CAN PROVE YOU NEED THEM. Y o u pr o b ab l y * don ' t need :
  7. • Heroku • IMGIX • Fastly CDN • Algolia Search

    • NewRelic • Coralogix • Github Actions • Docker Compose (locally) SHIFT'S STACK
  8. • Spikes in origin traf fi c • Slow HTTP

    responses • Blocked background queues • Attacks (malicious or accidental) • Wasting time on abandoned requests • Unexpectedly large inputs
  9. ~30% h i t r a t i o i

    nc r e a se re p r e s e n t ed ~85% d r op in ORIGIN TRAFFIC
  10. • Allowlist your query parameters • Sort them deterministically •

    Downcase your whole URL • Consider longer TTLs with purging
  11. • Fix your Cache-Control for CDN & browsers • `fresh_when`

    for conditional responses You don't want to render the entire body just to return that it's not modi fi ed! • Don't break the 'Back/Forward' cache • Even a 1 second TTL might bear the worst
  12. T hi n gs do n ' t ch a

    n ge a s ofte n a s y ou think CONSIDER EXPECTATIONS
  13. class SessionsController < ApplicationController rate_limit to: 10, within: 3.minutes, only:

    :create end class SignupsController < ApplicationController rate_limit to: 1000, within: 10.seconds, by: -> { request.domain }, with: -> { redirect_to busy_controller_url, alert: "Too many signups on domain!" }, only: :new end class APIController < ApplicationController RATE_LIMIT_STORE = ActiveSupport::Cache::RedisCacheStore.new(url: ENV["REDIS_URL"]) rate_limit to: 10, within: 3.minutes, store: RATE_LIMIT_STORE end class SessionsController < ApplicationController rate_limit to: 3, within: 2.seconds, name: "short-term" rate_limit to: 10, within: 5.minutes, name: "long-term" end Source: api.rubyonrails.org/classes/ActionController/RateLimiting/ClassMethods.html
  14. hydra = Typhoeus::Hydra.new 10.times do request = Typhoeus::Request.new("www.example.com", followlocation: true)

    request.on_complete do |response| # do_something_with response end hydra.queue(request) end hydra.run Source: github.com/typhoeus/typhoeus
  15. • Do the math on database connections max servers =

    max connections / workers / threads • Consider a database connection pooler (PgBouncer) • Scale up faster than down • Scale based on request queuing, not response time same goes for your background queues • Consider scheduling scale-ups manually speak to your (big) clients!
  16. default: &default adapter: postgresql encoding: unicode connect_timeout: <%= Integer(ENV.fetch("POSTGRES_CONNECT_TIMEOUT", 5000))

    %> checkout_timeout: <%= Integer(ENV.fetch("POSTGRES_CHECKOUT_TIMEOUT", 5000)) %> variables: statement_timeout: <%= Integer(ENV.fetch("POSTGRES_STATEMENT_TIMEOUT", 5000)) %> pool: <%= Integer(ENV.fetch("RAILS_MAX_THREADS", 3)) %>
  17. • HTTP caching & CDN • SLA-named background queues •

    Rate limits • Field constraints • Batched queries • Parallelised outbound calls • Autoscaling app & worker servers • Timeouts
  18. Subscription (ActiveRecord, noun) & ResumeSubscription (ActiveModel, verb) We probably don't

    need StartSubscription, CancelSubscription, UpdateSubscription etc...
  19. • Your API shouldn't re fl ect your database We

    learned and optimised a lot from CQRS • Idempotency is your friend Especially the friend of timeouts • Go back to basics on database schema design e.g. smaller tables, not everything needs a foreign key
  20. • You have to measure, but be sensible • Don't

    jump to blaming the tech • Sweat your existing tech stack • Protect fi rst
  21. Source: lorenstew.art/blog/progressive-complexity-manifesto We were taught a false binary. They said

    our choices were: 1. Static HTML: “Dead” pages with little to no interaction 2. Full SPA: “Modern” apps with baked-in complexity
  22. BEFORE AFTER • Huge client-side JS framework • 3rd-party dependencies

    • Naive prefetch logic • Rendering on navigation • Simple declarative JSON • No JavaScript necessary • Adapts to user device conditions • True prerendering See: developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API
  23. BEFORE AFTER • Huge client-side JS framework • Missing user

    feedback • Endless routing library churn • CSS-only for cross-document • 1 JS call for same-document • Native browser user feedback See: developer.chrome.com/docs/web-platform/view-transitions
  24. SCR oll -LINKED ANIMATION Z e r o J a

    v a S cr i p t (See also scroll-state() container queries)
  25. BEFORE AFTER • @radix-ui/react-accordion • react-modal • swiper.js • react-select

    • GSAP / Framer Motion • fl oating-ui • <details> • <dialog> & Command Invokers • CSS snapping & carousels • Customisable <select> • Scroll-driven Animation • Popovers, Interest Invokers, Anchor Positioning
  26. • First look to The Web Platform before NPM •

    Understand 'The Event Loop', how JavaScript executes in the browser • Monitor your Core Web Vitals with RUM, especially Interaction to Next Paint (INP)
  27. LFG: DoE / Sen. EM / DoPM / Sen. PM

    THANK YOU twnsnd.com/nwrug2025