Languages
Ruby
A language built for the people who read the code, delivered by engineers who keep running it long after launch.
Overview
Ruby is a dynamic, object-oriented language whose designer, Yukihiro Matsumoto, optimised it for one unusual thing: the happiness of the programmer writing it. That sounds like a slogan until you use the language and notice the decisions behind it. Everything is an object — integers, nil, even a class itself — so the rules stay consistent rather than riddled with exceptions. Blocks and closures let you pass behaviour around as easily as data. Metaprogramming lets code define methods and respond to messages at runtime. The result is a language that reads close to the intent of the person who wrote it, which is why a well-written Ruby method often needs no comment to explain what it does.
It is worth being precise about what this page is and is not. Ruby the language is famous almost entirely because of Ruby on Rails, the web framework that made it the default choice for startups and MVPs through the 2010s. But Ruby is not Rails. The language is used for command-line tools, background job processing, infrastructure automation, DSLs, data munging and plenty of software that never serves an HTTP request. We build Rails applications too, and that framework has its own page; here we are talking about Ruby itself — its object model, its blocks, its gem ecosystem and its runtime — because those are what you are actually committing to when you choose it.
We reach for Ruby when the scarce resource on a project is engineering time, not CPU time. Its expressiveness and the maturity of its libraries — distributed as gems and managed with Bundler — mean a small senior team can turn a rough idea into working, changeable software quickly, and keep changing it as the idea sharpens. We are equally blunt about the cost of that choice: the reference interpreter is slow next to compiled languages, its threading model limits true parallelism, and the talent pool has thinned considerably since Ruby’s hype peaked around 2013. Those are real constraints, and on the wrong project they are disqualifying.
Best for — Product teams building I/O-bound web and back-office software where developer velocity and readable, changeable code pay off more than raw runtime speed — staffed by seniors who wield Ruby’s expressiveness with discipline.
Why teams choose Ruby
Fast from idea to working software
Ruby’s expressiveness and mature gem ecosystem let a small senior team get a real, running product in front of users quickly, then reshape it as you learn. When the risk on a project is building the wrong thing, that speed of change is worth more than raw execution speed.
Code your team can actually read
Well-written Ruby reads close to plain intent, so the engineer who inherits it six months later spends their time understanding the business problem, not decoding the language. Readability is not a nicety here; it is what keeps the cost of change low as the codebase ages.
A deep, battle-tested library ecosystem
For most common problems — authentication, background jobs, payment integrations, file handling, API clients — a well-maintained gem already exists and has been run in production by thousands of teams. You assemble proven parts rather than rebuilding plumbing, which is the fastest honest route to a working feature.
Why businesses choose Ruby
- You are building a product whose requirements are still being discovered, and you value being able to change direction cheaply over premium runtime performance.
- Your workload is dominated by waiting on databases and external services, so the interpreter’s speed is not your bottleneck.
- You want a codebase your own engineers can pick up and read without a long ramp, and you will invest in the seniority that keeps Ruby clean.
- You accept the trade-off honestly: a smaller hiring pool and a heavier runtime, in exchange for development speed and long-term readability.
What we build with Ruby
The capabilities this technology is genuinely strong at — and what we most often build with it.
Everything is an object
Ruby’s object model is uniform to a degree most languages are not: numbers, strings, nil and classes are all objects that respond to methods. That consistency removes special cases and makes the language predictable — once you know how one object behaves, you know how they all do.
Blocks, procs and closures
Blocks are Ruby’s signature feature: chunks of behaviour passed to methods, used everywhere from iterating a collection to wrapping resources in setup-and-teardown. Combined with procs and lambdas, they let you factor out patterns cleanly instead of repeating them, and they are why idiomatic Ruby rarely needs a manual loop counter.
Metaprogramming and DSLs
Ruby can define methods, intercept missing ones via method_missing, and reopen classes at runtime. Used with discipline this builds expressive domain-specific languages — the readable, declarative style you see in test frameworks and configuration. Used carelessly it builds mysteries, so we treat it as a scalpel, not a default.
The gem ecosystem and Bundler
Functionality ships as gems, and Bundler pins exact versions so builds are reproducible across every machine and environment. We keep the dependency tree deliberate and current rather than sprawling, because every gem is code you now run and must keep patched.
Duck typing and dynamic dispatch
Ruby cares what an object can do, not what class it is. This makes code flexible and easy to compose, and it makes seams for testing trivial to create. The cost is that type errors surface at runtime, which we counter with strong test coverage on the paths that matter and, where it earns its place, Sorbet or RBS for static checking.
Mixins and modules
Rather than deep inheritance trees, Ruby shares behaviour by mixing modules into classes. This gives you composition over rigid hierarchies — cross-cutting behaviour lives in one module and is included where needed, keeping class relationships shallow and the code easier to reason about.
Use cases
Product back ends and APIs
Web application servers and JSON APIs where the work is mostly database queries and calls to other services — Ruby’s productivity shines and its runtime speed is rarely the limit.
Background and scheduled jobs
Email sending, report generation, webhook processing and data synchronisation run as background workers, keeping slow work off the request path while staying in one readable codebase.
Command-line tools and automation
Internal CLIs, deployment scripts, data migrations and one-off munging tasks, where Ruby’s expressiveness makes short, clear tools that the whole team can maintain.
Domain-specific languages
Configuration, test suites and workflow definitions written as small, readable DSLs built on Ruby’s blocks and metaprogramming — declarative on the surface, plain Ruby underneath.
When Ruby is the right choice
- Right when you are building a product whose shape is still moving — an MVP, a new line of business, an internal tool that will grow — and the ability to change the code cheaply matters more than squeezing the last millisecond out of a request.
- Right when the work is I/O-bound: web back ends, background jobs, integrations that spend their time waiting on databases and third-party APIs rather than grinding through computation. The interpreter’s raw speed rarely bites here because the bottleneck is elsewhere.
- Right when you have — or want us to provide — senior engineers who will use Ruby’s metaprogramming and blocks with restraint. The same features that make expert Ruby a pleasure to read make careless Ruby impossible to follow.
- Wrong for CPU-bound work — numerical computing, heavy data processing, anything that pins a core. The interpreter and the global lock make Ruby a poor fit, and you will spend your savings fighting the runtime.
- Wrong for systems programming, front-end code, or anything where a shrinking hiring pool is a serious operational risk. If you cannot picture staffing a Ruby team in three years, that is a legitimate reason to choose something more common, and we will say so.
Ruby: pros and cons
Strengths
- Exceptional developer productivity for I/O-bound product work — expressive syntax and a mature ecosystem turn ideas into working software fast.
- Genuinely readable code: the consistent all-objects model, blocks and sensible conventions make well-written Ruby easy to follow and safe to change.
- A deep gem ecosystem and Bundler give you reliable, versioned dependencies for almost any common problem, so you build features rather than infrastructure.
- Powerful metaprogramming lets you build clean domain-specific languages and remove boilerplate — a real asset in expert hands.
Trade-offs
- The reference interpreter (MRI/CRuby) is slow at raw computation compared with compiled languages — fine for I/O-bound work, painful for CPU-bound work.
- The Global VM Lock means threads cannot run Ruby code in true parallel on multiple cores; you scale with processes and memory instead, which costs RAM.
- Higher memory use than leaner runtimes: Ruby processes are not cheap, and horizontal scaling multiplies that cost across every worker.
- The hype cycle has faded since its 2013 peak, and the talent pool has shrunk with it — hiring Ruby engineers is harder and narrower than hiring for, say, JavaScript.
Architecture
A Ruby codebase lives or dies by its object boundaries. Because the language will happily let you put logic anywhere, the architectural discipline has to come from the team: clear domain objects with single responsibilities, service objects for multi-step operations, and a firm line between the business logic and whatever framework or web layer sits in front of it. We keep that domain code plain Ruby — testable without booting a server or a database — so the parts that encode your actual business rules stay independent of the delivery mechanism.
We use Ruby’s composition tools deliberately. Modules mix in shared behaviour rather than forcing deep inheritance; metaprogramming is reserved for genuine DSLs and boilerplate removal, never for cleverness that a future reader has to reverse-engineer. The aim is a codebase where the object model mirrors the problem domain, so that a new engineer can navigate by reasoning about the business rather than memorising framework magic. That is the difference between a Ruby project that stays cheap to change and one that quietly turns into folklore.
Performance
We will not pretend Ruby is fast at raw computation — the reference interpreter, MRI/CRuby, is slower per operation than compiled or JIT-heavy languages, and the Global VM Lock stops threads from executing Ruby code on multiple cores at once. For the I/O-bound work Ruby is actually used for, this matters far less than the benchmarks suggest, because a request that spends most of its time waiting on the database is not limited by interpreter speed. The honest performance story is that Ruby is quick enough for most product back ends and the wrong tool for anything CPU-bound.
When Ruby is slow in practice, the cause is usually the code, not the runtime: N+1 database queries, work done in a request that belongs in a background job, or memory bloat from loading too much at once. We profile before we optimise, move slow work off the request path, and let the database do what databases are good at. Where a genuine hot path is CPU-bound and unavoidable, we are honest that the right answer may be a C extension, a faster runtime, or moving that one job to another language — not heroics in Ruby.
Security
Ruby’s dynamic nature is a security consideration in its own right. Metaprogramming, eval and methods that turn strings into code are powerful and dangerous; we never feed untrusted input into anything that constructs or executes code, and we are strict about deserialising data from outside the system, because unsafe object loading has been the root of real Ruby vulnerabilities. Input is validated at the boundary and treated as hostile until proven otherwise.
Beyond the language, the gem ecosystem is the main attack surface. Every dependency is code you run with your application’s privileges, so we keep the tree small, pin versions with Bundler, and audit against known advisories with tools such as bundler-audit as part of the pipeline rather than as an afterthought. Secrets stay out of the code and out of version control, authorisation is enforced in the domain layer rather than assumed from the UI, and dependency updates are a routine, scheduled cost — not a panic when a CVE lands.
Scalability
Because the Global VM Lock prevents true multi-core threading in the reference interpreter, Ruby scales primarily by running multiple processes — more workers behind a load balancer, more job runners consuming a queue. This model works and is well understood, but it has a direct cost: each process carries its own memory footprint, and Ruby processes are not lean, so horizontal scaling is as much a memory-budget conversation as a throughput one. We plan capacity with that arithmetic in mind rather than discovering it on the hosting bill.
The architectural work that makes Ruby scale is keeping the application stateless so any process can serve any request, pushing slow and bursty work into background queues, and leaning on the database and a cache such as Redis to carry shared state. Done this way, a Ruby application scales out predictably for the I/O-bound workloads it suits. The one thing we will not do is promise it scales for CPU-bound computation — for that class of problem, adding Ruby processes is throwing money at the wrong constraint.
Ruby integrations & ecosystem
The technologies we most often pair with it — each links to how we work with it.
How we work
We start by separating the business logic from the framework, because in Ruby that boundary is what keeps a fast-moving product from calcifying. We build in thin vertical slices — a real feature, running in production, rather than a scaffold — and we lean on Ruby’s productivity to keep those cycles short. Tests carry more weight in a dynamically typed language than a statically typed one, so we cover the load-bearing paths properly, and where types genuinely reduce risk we add Sorbet or RBS rather than pretending the language checks itself.
The engineers who design your Ruby application are the ones who write it and keep it running afterwards. That is what we mean by operating what we build: the person choosing whether to reach for metaprogramming is the person who will have to read it in a year, which is exactly the right incentive. We favour boring, legible Ruby over clever Ruby, keep the gem list deliberate, and hand you a codebase your own team can maintain rather than one that depends on us to decode.
The service behind it
Delivered throughCustom Software DevelopmentWhat we build with Ruby
The disciplines this technology most often shows up in — from a first build to taking over and stabilising an existing one.
How we deliver
- 01
Discover
We map the system, the constraints and the business it serves — including the parts nobody documented.
Architecture brief
- 02
Architect
Decisions get made, written down and defended before a line of production code exists.
Decision records
- 03
Build
Short cycles against working software. You see progress in the product, not in a status deck.
Shipping increments
- 04
Operate
Monitoring, incident response and iteration. The system is alive, so the engagement is too.
Runbooks & SLOs
Industries we use Ruby in
Domain knowledge changes what gets built. A few of the sectors we know before the first meeting.
Also in Languages
Why teams choose us for Ruby
Senior engineers only
Ruby’s expressiveness rewards judgement and punishes showing off. The people writing your code know when to reach for metaprogramming and, more importantly, when not to — there are no juniors learning that distinction on your project.
We operate what we build
We run the Ruby systems we ship, so we optimise for the maintenance reality: legible code, a deliberate dependency tree, and a memory budget we understand before it becomes a hosting bill.
Honest about the trade-offs
If your workload is CPU-bound, or the shrinking Ruby talent pool is a real risk for your team, we will tell you before you commit — and recommend a different language if that serves you better.
Typical timeline
- 01
Discovery and domain modelling
One to two weeks establishing the domain objects, the integrations, and where the business logic sits relative to the framework — the decisions that keep a Ruby codebase cheap to change.
- 02
First vertical slice
Two to three weeks delivering one real feature end to end in production, proving the architecture and the integrations against reality rather than a plan.
- 03
Iterative build
Feature-by-feature delivery in short cycles, each shippable, with test coverage on the paths that matter and background work moved off the request path as we go.
- 04
Hardening and handover
Profiling for query and memory hotspots, a dependency audit, and documentation so your team can own and extend the code without us in the loop.
How pricing works
- Fixed-scope builds for well-defined products — an MVP, an API, an internal tool — quoted once we understand the domain and the integrations involved.
- Monthly senior engagement for ongoing product work, where the requirements evolve and you want continuity and someone who operates the system, not just ships it.
- Focused audits and rescues for existing Ruby codebases — performance, a runaway memory footprint, tangled metaprogramming, or a stalled project — priced by the assessment.
Hire Ruby engineers
Need Ruby capacity on your own team? We embed named senior engineers into your existing team — reporting to your leads, working in your rituals — so you add capacity without a hiring cycle.
Hire Ruby engineersCommon questions
Is Ruby the same as Ruby on Rails?
No. Ruby is a general-purpose programming language; Rails is a web framework written in Ruby. Rails is why most people know the language, but Ruby is also used for command-line tools, background jobs, automation and domain-specific languages that never touch the web. You can use Ruby without Rails, though the two are often chosen together. Rails has its own page — this one is about the language.
Is Ruby too slow for production?
For the I/O-bound work Ruby is normally used for — web back ends, jobs, integrations — no. Those workloads spend most of their time waiting on databases and other services, so the interpreter’s raw speed is rarely the bottleneck. For CPU-bound work like heavy computation or data crunching, Ruby genuinely is a poor fit, and we would steer you elsewhere. The honest answer is that Ruby is fast enough for most products and the wrong tool for a specific, identifiable class of them.
What does the Global VM Lock mean for us?
The GVL prevents Ruby threads in the reference interpreter from running Ruby code on multiple CPU cores simultaneously, so you cannot get true parallelism from threads alone. In practice you scale Ruby by running multiple processes, which works well for I/O-bound applications but uses more memory. It only becomes a hard limit for CPU-bound, parallel computation — which is one of the cases where we would recommend a different language rather than fighting the runtime.
Is it still worth starting a new project in Ruby in 2026?
For the right project, yes. Ruby’s hype has faded since its 2013 peak, but the language and its ecosystem are mature and stable, and its productivity for I/O-bound product work is still excellent. The real consideration is hiring: the talent pool is smaller than for JavaScript or Python, so if you cannot realistically staff a Ruby team over the next few years, that is a legitimate reason to choose something more common. We will be candid about which side of that line your situation falls on.
How do you keep a dynamically typed Ruby codebase safe to change?
Two things carry most of the weight. First, disciplined tests on the paths that matter, because a dynamic language moves the safety net from the compiler to the test suite. Second, restraint with metaprogramming, so the code stays readable rather than magical. Where static typing genuinely reduces risk in a large or critical codebase, we add Sorbet or RBS, but we use them because they help, not as a reflex.
Building on Ruby?
A technical conversation with the engineers who would do the work. If we are not the right fit, we will say so on the call.