The Honest Retrospective#
I spent a few weeks learning Ruby on Rails as a TypeScript developer with Java experience, building a URL shortener along the way. This is the post where I stop explaining Rails and start reflecting on what the experience was actually like — the surprises, the frustrations, and the opinions that shifted.
What Was Harder Than Expected#
The Implicit Everything#
Ruby loves being implicit. Implicit returns. Implicit rendering. Implicit loading. No imports. For the first few days, I constantly felt like the code was doing things I didn’t ask it to do. A controller action with no render call somehow renders the right template. A class I never imported just… exists.
This discomfort is temporary. Once you learn the conventions — controller name maps to view folder , class name maps to file path — the implicitness becomes predictability. But that first week felt like coding blindfolded.
Debugging Without Types#
TypeScript’s type system catches errors at compile time. Ruby doesn’t have that. A misspelled method name, a wrong argument count, a nil where you expected an object — these are all runtime errors in Ruby. You find them when the code runs, not when you write it.
RBS (Ruby’s type signature system) exists, but adoption is low and it’s opt-in. The practical substitute is tests. Where TypeScript catches bugs with types, Ruby catches them with RSpec. Different safety net, similar outcome — but the feedback loop is slower (run tests vs instant red squiggles in the editor).
The Gem Ecosystem Is Smaller#
npm has 2+ million packages. RubyGems has ~180K. The popular tools are excellent (Devise for auth, Sidekiq for jobs, RSpec for testing), but for niche use cases, you might not find a gem. In TypeScript, there’s a package for everything. In Ruby, sometimes you write it yourself.
What Was Easier Than Expected#
The Rails Learning Curve#
I expected a steep learning curve. Instead, it was a steep confusion curve for the first few days, followed by rapid productivity. Once I accepted the conventions, things moved fast. rails generate scaffold gave me a working CRUD app in one command. Migrations handled schema changes. ActiveRecord made queries readable.
Compared to setting up an Express app from scratch — choosing a router, an ORM, a validator, a template engine, and wiring them all together — Rails got me to “something works” much faster.
Ruby Is Just… Pleasant#
The syntax grows on you. Implicit returns make code concise. Blocks read naturally. String interpolation with #{} is cleaner than template literals (no backtick fumbling). nil being an object means fewer null checks. unless reads like English.
return unless order.valid?
# vs TypeScript
if (!order.valid) return;
Both do the same thing. The Ruby version reads like a sentence. After a while, the TypeScript version starts feeling unnecessarily mechanical.
Testing Infrastructure#
Rails’ testing ecosystem is mature and well-integrated. RSpec + FactoryBot + WebMock + SimpleCov gives you everything Jest + custom factories + MSW + istanbul gives in TypeScript. But it’s more cohesive — everything works together out of the box, with established conventions for spec file locations, factory definitions, and test configuration.
The let / subject / context pattern in RSpec is genuinely more expressive than Jest’s beforeEach for setting up test scenarios. Once I learned the idioms, writing tests felt faster than in Jest.
Opinions That Shifted#
On “Convention Over Configuration”#
Before: I liked explicit configuration. If I can’t see the wiring, how do I know what’s connected?
After: Conventions are just implicit configuration that everyone agrees on. When the convention is strong enough (model name = table name = controller name), the explicitness adds noise, not clarity. I don’t need a file that says UrlsController routes to Url model when the names already tell me that.
On Server-Rendered HTML#
Before: Server-rendered HTML is outdated. Modern apps need React/Vue/Svelte for interactivity.
After: Hotwire handles 80% of the interactivity I’d normally reach for React. Page transitions feel instant, form submissions update without full reloads, and the JavaScript bundle is 15KB instead of 200KB. For the remaining 20% — complex interactive UIs — I’d still use React. But most apps are in that 80%.
On Ruby’s Type System (Or Lack Thereof)#
Before: No types = chaos. How do you refactor safely?
After: The test suite IS the type system. If your tests are comprehensive, refactoring is safe. If they’re not, types wouldn’t save you anyway (types catch type errors, not logic errors). The tradeoff is real — TypeScript catches a class of bugs that Ruby doesn’t — but it’s smaller than I expected.
On Framework Choice#
Before: The best framework is the one you customize to fit your needs.
After: The best framework is the one that makes the most decisions for you correctly. Rails’ opinions are well-tested defaults that work for 90% of web apps. The 10% where you need to break convention, Rails lets you. But you shouldn’t start by breaking convention — a pattern I recognized from my infrastructure work .
What I’d Tell Myself Before Starting#
Stop looking for the import statement. Zeitwerk handles it. Class name = file path. Trust it.
Write tests early. Without types, tests are your safety net. Write them before you’re comfortable with the syntax — the test failures will teach you Ruby faster than any tutorial.
Follow the conventions first, question later. Rails conventions feel arbitrary until you see how they eliminate entire categories of decisions. Try them as-is for a week before customizing.
0is truthy. Seriously, put a sticky note on your monitor.Read the Rails Guides, not Stack Overflow. The official guides are some of the best framework documentation in any language. Stack Overflow answers are often outdated (Rails evolves fast).
It’s not that different. MVC is MVC. REST is REST. Tests are tests. The patterns are the same — the syntax and conventions are what change. The switching cost is lower than you think.
The Big Picture#
Learning Rails didn’t replace TypeScript in my toolkit. It added a new tool alongside it. Some projects fit Rails perfectly (content-heavy apps, admin panels, rapid prototyping). Some fit TypeScript better (API-heavy services, real-time apps, projects where the team knows TypeScript). The right tool for the job — same lesson, different context.
The biggest value wasn’t learning a new framework. It was seeing the same problems solved differently. Convention over configuration. Server-rendered HTML instead of JSON APIs. Implicit behavior instead of explicit wiring. None of these are right or wrong — they’re tradeoffs. And understanding both sides makes you better at choosing.
