Blog
0 pixels scrolled
  • Home
  • Work
  • Services
  • About
  • Blog
  • Contact
From Developer to Rails Developer,
and Then Some
Benjamin Wil on September 26, 2023
From Developer to Rails Developer,
and Then Some
Benjamin Wil on September 26, 2023
Posted in Rails
← Back to the blog

It isn’t until I’m pair-programming with someone who is new to Ruby on Rails that I remember the pain. I have so much weird muscle memory for Ruby and Rails and all of the tools that come along with Ruby and Rails. I take it all for granted now; I’m an experienced Rails developer.

You may have read about “the magic” of Rails. You may have even experienced it. But did you experience it à la carte? In order to “learn Rails” you must also learn, in many cases, Factory Bot, RSpec, YARD, Active Job, and Capybara. You may need to learn how Rack middleware works. You may need to write some YAML, or read something called a “VCR cassette” that outputs many large YAML files that are “fun” to “read”. You may even need to learn your coworkers’s preferred, weird ways of writing Ruby (a very expressive language) that you just haven’t encountered yet. This all can be overwhelming.

Getting un-lost

So, as an experienced Rails developer, I take for granted how easily I can figure out how to do stuff. And how I can figure out how to do stuff if I don’t know how to do. I take for granted that, when you’re new, you haven’t learned where to look yet. None of this, by the way, is unique to the Ruby and Rails ecosystems. Maybe you’ve gone through this “learning how to learn” process a few times already. Maybe you like it now.

Whether you’re a junior developer or an experienced developer who is just new to Ruby, the road from feeling completely lost to feeling able to move forward may be long. My aim in this post is to give you a small jump-start. To point you in the direction of what you need to learn and the resources that can help you learn it. So you, too, can take your past self for granted and leave that completely lost feeling behind.

Because my employer, Super Good, primarily works on Ruby on Rails applications that use the Solidus ecommerce framework, some parts of this article point to stuff I do as a Solidus developer, and stuff I’ve learned because I’m a Solidus developer. But if you don’t care about Solidus, don’t let that sour you on this blog post. At the end of the day, Solidus is just another Rails engine. As a Ruby developer, and a Rails developer, you inevitably will need to locate and understand source code that comes from external dependencies. I hope you find that I’ve written this guide in a way that equips you with the knowledge you need regardless of which gems are listed in your Gemfile.

The Rails developer checklist

Before we get deep into things, I want to provide you with my opinionated, high-level checklist. Take it or leave it. It will take you more than a day, maybe more than a month, to confidently check off all these items, depending on your starting point and your end goals. After many years using Ruby and Rails, I’ve identified each of these items as things that really helped me level up and feel more productive at work:

  • Understand the basics of Ruby, the language, and how to read Ruby code. 🔗
  • Learn Ruby’s Enumerable and Array classes. 🔗
  • Get a handle on Ruby’s Module#prepend, Module#include, Module#extend, Module#class_eval and the super keyword. 🔗
  • Understand Ruby on Rails, the framework, and the tools it gives you out-of-the-box. 🔗
  • Start learning about the testing tools and infrastructure Rails gives you. 🔗
  • Build your knowledge of Ruby testing tools you’ll use all the time. 🔗
  • Learn how to efficiently locate and debug code that comes from Rails and other dependencies. 🔗
  • And finally, learn to “safely” extend and override when dependencies get in your way. 🔗

These things build on one another, but not in a strictly linear way. You may have to go back and relearn stuff (Module#prepend versus Module#include?) as you move forward. That said, read on if you want to know what each of these items really entails.

Skip to the end if you just want my recommended reading list—formatted as a three-week learning plan.

How to learn Ruby

If you’ve made it this far, I will assume that you have previously learned another programming language or are programming language-curious. If you’re unsure how to describe the difference between true and "true", this blog post is not for you. And I’d recommend you go read “How to Pick a Programming Language to Learn” instead. Ruby would be a great first programming language to learn. With that said, my following recommendations will be more useful to someone who already sort of knows how programming languages work.

If you’re looking to learn Ruby, I’d start at Ruby’s official website, which is the home of two great articles: “Ruby in Twenty Minutes” and “Ruby from Other Languages”. One or both of those articles may be useful in your path to becoming fluent in Ruby.

But the things that I found the most valuable early on in my Ruby journey was the the Edgecase Ruby Koans. Going through each of these tests, from start to finish, is an engaging, interactive way to get familiar with how to read and write Ruby. The Ruby Koans are so helpful, in fact, that I recommend that you do them all twice: once when you’re just starting, and once after six months or a year of writing Ruby somewhat regularly.

I also want to highlight some text on the Ruby Koans homepage:

Testing is not just something we pay lip service to, but something we live. It is essential in your quest to learn and do great things in the language.

I can’t speak for all Rubyists, but in relation to my own Ruby journey: this is true.

“Learning Ruby,” never stops, but it may take you a day or two before you go on to next item in the checklist. It’s okay if you don’t fully get Ruby, or understand everything the Koans have taught you. But in any case: good luck.

Enumerables and arrays

Now that you can read and write Ruby, spend some time reading the Enumerable and Array core library documentation. As a Rails developer, you will deal with arrays a lot. And an instance of an Array not only has all of the standard array methods available on it, but also all of the Enumerable methods, since an array is a type of Enumerable:

my_array = Array.new
my_array.is_a? Array #=> true
my_array.is_a? Enumberable #=> true

While you read through this documentation, I recommend keeping an open Ruby console nearby so you can try each method out. In my experience, manipulating dead-simple objects is a quick way to test assumptions:

my_array = [
  [1, 2],
  [3, 4]
]

my_array.flat_map { |item| item * 2 }
#=> [2, 4, 6, 8]

By default, when you install Ruby, you can run irb to get into an interactive Ruby console. But I usually recommend installing Pry to take advantage of its advanced features. (Later I will recommend Pry as a great debugger for other reasons, too, but let’s leave that until later.)

Prepends, includes, and friends

As you read and write more Ruby, you will notice that inheritance comes up a lot:

require "date"

class Document
  attr_accessor :name, :author, :published_at

  def initialize(name:, author:, published_at: ::DateTime.now)
    @name = name
    @author = author
    @published_at = published_at
  end
end

class BlogPost < Document
  def pretty_date
    [published_at.year, published_at.month, published_at.day].join "-"
  end
end

my_blog_post = BlogPost.new name: "Good blog post",
  author: "benjamin"
  published_at: DateTime.new(1990, 01, 01)

my_blog_post.pretty_date
#=> "1990-1-1"

And in many ways:

module DateHelperMethods
  def really_old?
    published_at < Date.new(2000, 01, 01)
  end

  def pretty_date
    "Published at: #{super}"
  end
end

Document.include DateHelperMethods

my_blog_post.really_old? #=> true
my_blog_post.pretty_date #=> "1990-1-1"

BlogPost.prepend DateHelperMethods

my_blog_post.pretty_date #=> "Published at: 1990-1-1"

Even if you don’t know Ruby yet, you probably have some idea of what’s going on in the above code snippets. But as you get deeper into real application and library code, you may need to know what makes including a module on a Ruby class different from prepending or extending a module on a class. There are many great blog posts about this, so I won’t go into any more detail here. Just keep this in mind as you move forward on your Ruby journey.

In the above snippets, we covered Module#include, and the super keyword. Module#prepend and #extend will will be in the same wheelhouse, but Module#class_eval is a bit different. If you will be working on a legacy application (and, especially, a legacy Solidus application), you may encounter Module#class_eval as well—so be sure to check that out, too, if that’s where you’re coming from.

Understanding Ruby on Rails

Now that you have some Ruby foundations, you can see what Rails offers you as a developer. If you’re feeling adventurous, you could even jump into Rails’s source code (maybe start with the activesupport gem) and make some sense of it.

The Rails Guides can give you a better idea of what Rails is than I can. So I suggest you start with their “Getting Started with Rails” article. It may take you a full day to meaningfully go through this article, but I think it’s worth the time. As a Rails developer, you’ll need to understand what models are, how controllers and routes work together, how views work, and how all of these layers connect to one another. As you go through this Rails guide article:

  • You’ll learn about the essential things you get “for free” when you’re running a Rails server.
  • You’ll learn about how Rails models interact with your database.
  • You’ll learn how database migrations work.
  • You’ll learn how to build your first model, controller, and views to create an interactive CRUD (create, read, update, delete) feature for some sweet app resource.
  • You’ll learn about Rails configuration and conventions, and stuff that will become second nature to you on your Rails journey.

I recommend following along with a terminal open as you go. Run rails new for yourself as you’re reading about it. Generate a database migration. Generate a new model. See what it feels like to create and run a fresh Rails application.

With some Rails basics in hand, you can start work on your real Rails application. You may also just want to source-dive into some open source Rails applications to see what real, in-the-wild feature code can look like. Check out the Mastodon git repository or the Discourse git repository, for example.

Testing Rails applications

While you’re checking out a Rails application codebase, don’t forget to examine its test suite. Every Rails app is different, and when it comes to tests some Rails apps use Ruby’s built-in test framework, Minitest and others use RSpec. There are many other Ruby testing tools that you may or may not encounter. Sometimes when you’re reading RSpec tests (colloquially known as “specs”) with FactoryBot factories sprinkled in, you may not even feel like you’re reading Ruby code anymore.

No matter what testing toolkits you or your colleagues use, you will want tests for each layer of the Rails application: the model layer, the controller layer, the view layer—and probably some other layers, depending on the app in question. Fortunately, Rails comes with testing mechanisms to make this job easier.

I recommend reading the Rails Guides’s “Testing Rails Application” next. That guide is tailor-made for Rails applications using Minitest. But in my experience, many Rails applications use RSpec (and rspec-rails) instead of Minitest. Put reading the RSpec documentation in your tentative to-do list.

Ruby’s many testing frameworks and tools

If you’re new to Ruby, learning Ruby and Rails and RSpec and all of the other testing tools being used within your RSpec tests, all at the same time, will be painful. If you look at Mastodon’s rails_helper file, which defines some functionality that gets included before many of the application’s test files are run, you will encounter references to a bunch of stuff that you can’t make sense of without reading a lot more documentation, and a lot more source code.

As with all toolboxes, you’ll come to recognize the tools that Rails developers reach for most often. You’ll see something like this:

before do
  create :blog_post,
    title: "Good blog post",
    author: create(:author, name: "benjamin")
 end

And you’ll just know: “Oh, the person who wrote this is using the FactoryBot #create method to create an blog post object. If I need to know what class that object is, I should go find where the :blog_post factory is defined. And blog posts have an association, #author, which has its own :author factory. All of this is happening before the test runs.”

But, yeah, you won’t know any of that at first.

Even worse: maybe the factory isn’t defined in your application, and it’s being included from some third-party library in your Gemfile. How do you locate stuff when you don’t even understand what it is to begin with? We’ll hopefully answer that question in the next section, about getting comfortable with your debugging tools.

In this post I haven’t even begun to cover things like RSpec’s mocking utilities or tools like VCR which can totally change how and what you test. For a while, you can get away with just knowing that these tools exist, and recognizing them when you see them in passing. But eventually, you’ll need to sit down with each of these tools and their READMEs individually.

Get comfortable with debugging tools and techniques

If you just finished reading the previous section and feel overwhelmed by the amount of things you haven’t learned yet, I bring good news: your debugging tools not only help you resolve bugs in software, they help you find solid ground when you don’t know where to look next. In the end, you need to debug and find your footing in the way that works for your brain (and your text editor). I just hope that you can:

  1. Use debugging tools to gather more context, quickly.
  2. Use debugging tools in tandem with your text editor or IDE (integrated development environment) to get to the source code of external dependencies quickly and easily.

When I work on Rails apps, this boils down to using Pry and bundle open, for the most part.

Earlier I mentioned Pry, an alternative interactive shell and REPL (read-evaluate-print loop) to the Ruby’s built-in one, IRB. IRB is really nice, especially on newer Rubies like 3.1 and 3.2. But Pry comes with some tools that IRB doesn’t come with. And, without any configuration, sometimes seems to just format things the way my brain works a little better than IRB does. (But your mileage may vary.)

Pry’s killer feature, in my opinion, is show-source—or $, for short:

[3] pry(main)> $ my_order.paid?

From: /Users/benjaminwil/Dev/my_rails_app/vendor/bundle/ruby/3.1.2/gems/solidus_core-3.2.6/app/models/spree/order.rb:483:
Owner: Spree::Order
Visibility: public
Signature: paid?()
Number of lines: 3

def paid?
  %w(paid credit_owed).include?(payment_state)
end

In a few seconds, we’ve just located the place in the source code where the method was defined. When you’re working with a complex library like Rails or Solidus, which gives you and your team access to hundreds of thousands of lines of code, this is a godsend. If I were only allowed to use one Pry feature, it’d be this one.

If I want to know more about what goes into making my_order.paid? return false, I now have so much more context than I had before. I can now also run bundle open solidus_core to open solidus_core in my text editor and surf the app/models/spree/order.rb file for more context if I need it.

Sometimes, the result of show-source will be less straightforward, and you may not know what to do with the context you’re given:

[4] pry(main)> $ my_order.total

From: /Users/benjaminwil/Dev/my_rails_app/vendor/bundle/ruby/3.1.2/gems/activemodel-6.1.7.2/lib/active_model/attribute_methods.rb:254:
Owner: Spree::Order::GeneratedAttributeMethods
Visibility: public
Signature: total()
Number of lines: 3

CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
  attr_names.flatten.each { |attr_name| define_attribute_method(attr_name, _owner: owner) }
end

But you can puzzle out a lot with this: “Oh, this comes from the activemodel gem, which seems like something I read about in the Rails Guides. The method definition makes me think that this is actually a generated method. So maybe that has to do with how #total is some kind of database-backed, Rails method defined with special Rails model magic. Interesting.”

It would be negligent of me not to mention that there are other debugging tools other than Pry, including Ruby’s own debug gem, which are powerful and good. I offer you Pry specifically because a) it’s a tool that the Super Good team and I use frequently and b) you can get up and running without having to learn much at all. I recommend checking out Pry’s screencasts and the devhints.io Pry cheatsheet if you want to get a deeper introduction to this tool.

As you continue your Rails journey, you’ll learn to call #to_sql after query methods, read your server logs more efficiently, and find Your Own Best Friends when it comes to making sense of Too Much information. So I’ll leave you there to figure that out for yourself.

Extending and overriding your Ruby dependencies

Depending on what kind of application you’re going to work on, you may or may not be extending functionality provided by external libraries. At Super Good, we often must adjust (or clobber) ecommerce functionality provided by Solidus so that our clients can get the most out of their completely-custom online stores and order management platforms.

Ideally, the frameworks and libraries you’re using provide extension points, or other configuration options that let you add code that does whatever you want without bypassing the library itself. In the real world, though, things don’t always line up like that. The Solidus Guides provide a handful of customization-specific guides: one of them discusses extension points and overrides. So, yeah. It’s canon. Sometimes you just gotta do it. But try not to if you can help it.

I can’t rightly talk much more about extension points. Every library’s extension points look a little different. The best ones have documentation that help you use them, or tell you how not to use them. In Solidus, there’s a whole class, Spree::AppConfiguration full of extension points. Many of them provide a default class or function that you can swap out for code that you keep in your own application:

module Spree
  class AppConfiguration < Preferences::Configuration
    # ...

    # Allows providing your own class for merging two orders.
    #
    # @!attribute [rw] order_merger_class
    # @return [Class] a class with the same public interfaces
    #   as Spree::OrderMerger.
    class_name_attribute :order_merger_class, default: 'Spree::OrderMerger'

    # ...
  end
end

If you wanted to provide your own order merger functionality, you can set that configuration option in an initializer:

# config/initializers/my_application.rb

Spree::Config.order_merger_class = "MyApplication::OrderMerger"

Then, wherever you see Spree::Config.order_merger_class in the Solidus source code, you’ll be using your class instead of the default one provided by Solidus.

In a section above, we already discussed how it’s worth understanding how Module#include, Module#prepend, and friends, work so you can understand where Ruby functionality is coming from when you read and write new features. If you need to change an existing class or module, or one that you’re inheriting from a dependency, these tools can help—but be careful, as you really are overriding the authored behaviour.

Since Rails 6, there are more easy-on-the-eyes, easier-to-skim ways to prepend additional functionality onto existing classes and modules using ActiveSupport::Concerns:

module CustomBlogPostStuff
  extend ActiveSupport::Concern

  prepended do
    has_many :tags
    has_many :categories, through: :tags
  end

  def pretty_date
    "Published at: #{super}"
  end
end

BlogPost.prepend CustomBlogPostStuff

My advice is to always prefer isolating your application code from code provided by dependencies. It makes gem upgrades more straightforward. It is less error prone generally. When you do resort to overrides, fight Rails and your dependencies as gently as possible. Also don’t forget that, when it comes to re-defining existing methods: super is a good friend to have.

The full reading list (and a three-week plan)

If you’re just beginning your Ruby or Rails adventure, I hope this post has been helpful and encouraging. Below I’ve included the links scattered throughout this article into a “three-week plan” of sorts.

Week 1

Learning Ruby:

Recommended tools for week 1:

Week 2

Learning Rails:

Ruby testing tools to familiarize yourself with:

More fun with Pry:

Real, open source Rails application codebases:

Week 3

Extra credit:

Cover photo by lilartsy on Unsplash.
Work ServicesAboutBlogCareersContact


Privacy policyTerms and conditions© 2023 Super Good Software Inc. All rights reserved.