Preface
(Note: this post builds off of my previous post, Learning to Learn, and references it a few times—however, this post should still make sense if you missed the first one. Also, this is not a tutorial, but rather a log of my experience learning Ruby on Rails.)
I recently found myself in a situation where I had 7 days to learn Ruby on Rails. This was mentioned briefly in my previous blog post, but I wanted to take the time now to elaborate on this experience.
My last blog post was all about how to learn, and taking the best approach to learning. This blog post is about putting those ideas into practice—a case study.
The Interview
First things first—I was in the midst of an interview process. It was a Friday afternoon, and next Friday I would be taking part in a live coding exercise. Most of this exercise would involve working on a Ruby on Rails application. This meant that I had a 7 day crunch to learn the framework.
You may speculate on how I found myself in this situation. Maybe it was the result of some forgery or deceit. Maybe I had lied about my experience in order to get my foot in the door, and was now left scrambling. But alas, no. It was not all that exciting. There was no dishonesty, and no nefarious behaviors. Instead, it was a shared understanding between me and interviewers that I was bringing no preexisting knowledge of Rails.
The agreement was that I would learn a new technology, and they would judge me on how well I picked it up. I would learn, but not master, the technology, and they would take my greenness into account while assessing my knowledge.
As such, I reasoned that I wasn’t really being tested on my knowledge of the Rails framework. Of course, when the time came, I would have to demonstrate some degree of functional knowledge. However if that was all that they were looking for they could have just quizzed me—given me a competency test or something of the like. But instead, they wanted to investigate a much more important skill—they wanted to watch me learn.
This ability to learn new technologies is fundamental to being a successful software engineer.
Yet, as important as it may be, gauging someone’s ability to learn is difficult! You can’t fully understand an individual or the soft skills they might have from a simple résumé or cover letter. And technical interviews, no matter the rigor, end up missing the point as well.
Image by Vincent Déniel, on Twitter @vincentdnl
I’ve never felt represented by technical assessments, or interviews which follow silly methods (like the STAR method). But by including a learning process in the interview (i.e., learning Ruby on Rails), it gave me a chance to fully represent myself. It benefited both of us. They were able to test me on some very important, yet hard to quantify skills, and I was able to represent myself in a way that I typically would have not been able to.
The Approach
As I thought about the interview and what I was really being evaluated for, I started to form a strategy—a mind set.
The way in which I would approach the next 7 days was very intentional.
I set out to take a higher-level approach to the learning, and, throughout the process, remind myself to focus on the concepts, the bigger picture, and how the things I’m learning relate to my existing knowledge. This was new ground for me. For the first time in my career, I was not just aware of what I was learning, but how I was learning.
To better summarize some aspects of the approach I would be taking:
Focus on the concepts—think bigger
For example, take the idea of inheritance. Low level thinking might sound like, “All the other classes are adding < ApplicationController
if they want to use this method, so I need to do that too. Otherwise it throws an error.’’
But high level thinking might be more like, “I need to access the ApplicationController class’ methods. Ruby supports inheritance, so I’ll add < ApplicationController
to the class definition to let the compiler know that I’m inheriting it.”
Compare and contrast
This could be very simple things, like “In Python it’s None
, in Javascript it’s null
, but in Ruby it’s called nil
”, or making connections like “oh, this is like Rail’s version of pip
, or npm
.”
Be curious
“I haven’t seen anything about string interpolation, I wonder if Ruby supports some version of that?”
“How would I do something like this in Python? Javascript? Would it be easier or harder?”
Anticipate
“Rails generates a lot of functions and methods for you that aren’t defined in the code—I bet that will be hard to debug.”
Be active
I like to take handwritten notes to make sure that I’m not daydreaming, or just moving my eyes over words. If I’m being really purposeful, I like to do the little bonus activities and exercises that tutorials or textbooks often include. Sometimes they’re silly, and feel wasteful, but they do force me to be active, to think about, and to use what I’m learning.
Take it slow
Everyone has their own pace, and increasing that pace too much can degrade the other aspects of learning. I find that when I speed up too much, I stop taking notes and start skipping over useful activities. I get lazy, and I don’t learn as much. Moving at a healthy pace, and also taking breaks when needed is very important.
The Approach (continued)
Being able to take a step back and approach a problem at a high-level like this was a significant milestone for me. Simply put, when you’re learning your first programming language, you don’t have anything to compare it to. The only things that you can focus on are the low-level details—things like syntax, iteration, and data structures.
It’s possible, however, that a learner never graduates from this mindset. A developer could know three, four, or more languages on a functional level, but still be unaware of the concepts.
It might be that up until this point, I just didn’t have enough experience to see the bigger picture. Or, though I hate to say, maybe I was the developer who never graduated from the mindset of memorizing low-level details. Or it could’ve been that I was thinking about concepts, taking apart information, and categorizing knowledge, but I just wasn’t very conscious that the process was taking place.
But for whatever reason, when I set out to learn Ruby on Rails, I was acutely aware that I had a new approach to learning. It’s like I had hiked up to a new vantage point. After hacking my way through a forest where all I could see was the vines and foliage, I now stood atop a mountain, with a clear view of where I had been, and the trails I had walked.
Armed with my new mindset and approach to learning, I couldn’t wait to start.
Resources
Michael Hartl’s Ruby on Rails Tutorial was the main resource I used when learning Rails. It’s a very impressive learning tool that leads you through building what is essentially a Twitter clone. Unfortunately it’s not free (apparently it used to be—I guess I missed out on that), however I believe that the $31 price tag to gain access to the online version is well worth the investment.
I also consulted the Rails API Docs a few times when I was feeling curious, or looking for more specific information on a certain Rails module. Thanks to the tutorial, I didn’t really need to dive too much into the docs, but I think it’s always good to get familiar with the source documentation of any language or framework you’re using.
And of course Google, which led me to Stack Overflow, GitHub issues, and random blog posts.
Setting Up The Environment
I started by downloading Ruby, which brought me my first few lessons.
Firstly, I learned that Ruby is a programming language! Well, I sort of already knew this, but before this moment some smooth-talker could have convinced me otherwise. You know when you kind of think you know about something, you’ve heard people talk about it, but you don’t actually know about it? I had never used Ruby, and I had never really had a reason to. So as elementary as it may sound, this is where I started—with the recognition of Ruby as a language.
Building on that knowledge, I learned that Rails is a web framework that is written in the Ruby programming language. Now we’re getting somewhere! I’m starting to understand the components that make up Ruby on Rails, and I can relate them to what I already know. Saying Ruby on Rails is kind of like saying Javascript on React (although that doesn’t quite have the same ring to it, does it?).
My next run in with curiosity—is there a version manager I should be using?
I am familiar with nvm
and pyenv
for managing Node and Python versions, respectively, however the tutorial I was following did not mention any Ruby version manager. I took to the internet and found the highly popular, rbenv
.
Perfect.
With rbenv
in hand, installing Rails came easily and generating a project was a breeze. I found the rails new
command to be powerful, automated, and that it gave me everything I needed to get a project up and running in the browser. It’s always nice to see some sort of early success, even if it’s “Hello world!”, or in this case, “Yay! You’re on Rails!”. I likened it to my experience with npx create-react-app
, which generates a simple React application.
Directories and File Structure
My first impression after generating a project—“There’s a lot going on.”
Generating a Rails application resulted in 46 directories and subdirectories, and 89 files. Some of these files were familiar to me, like a README.md
, and the public/
folder which holds the standard 404.html
and favicon.ico
. But most of everything else was uncharted territory.
To make things simpler, we can try and strip down the excess and focus on the core of the app. Arguably the most important directory—where you can find the meat and potatoes of the application—is the /app
directory. But even there it’s hard to get an idea of what’s going on.
Here’s what you see in the /app
directory after generating a new application—
├── app
│ ├── assets
│ │ ├── config
│ │ │ └── manifest.js
│ │ ├── images
│ │ ├── javascripts
│ │ │ ├── application.js
│ │ │ ├── cable.js
│ │ │ └── channels
│ │ └── stylesheets
│ │ └── application.css
│ ├── channels
│ │ └── application_cable
│ │ ├── channel.rb
│ │ └── connection.rb
│ ├── controllers
│ │ ├── application_controller.rb
│ │ └── concerns
│ ├── helpers
│ │ └── application_helper.rb
│ ├── jobs
│ │ └── application_job.rb
│ ├── mailers
│ │ └── application_mailer.rb
│ ├── models
│ │ ├── application_record.rb
│ │ └── concerns
│ └── views
│ └── layouts
│ ├── application.html.erb
│ ├── mailer.html.erb
│ └── mailer.text.erb
This was the dark side of automation. When the work is done for you, you don’t have to understand it.
But as I followed the tutorial, I began to pick up on some familiarities. I was at first confused by the .html.erb
file extensions littering the project, but then learned that ERb
is an HTML file with Ruby code embedded in it. That makes sense—I’m familiar with the concept from PHP files, or the JSX that’s used in React. And the Gemfile is the equivalent of other manifest/dependency files that I’m used to such as Node’s package.json
and Python’s requirements.txt
.
These are just a couple examples, but similar thought processes followed.
There was certainly a learning curve, and it took me a while to get comfortable, but the Rails directories structure began to make sense over time. And (spoiler alert!) it turns out that this standard directory structure is actually one of my favorite things about Rails. It lets you hop into almost any Rails project and quickly get a sense for what’s going on. This seems to be a common theme with Ruby on Rails—something seems very intimidating or confusing at first, but after getting over the learning curve, it becomes a very useful element of the framework.
Model-View-Controller
I had never used the Model-View-Controller design pattern before Rails. I had heard of the idea from Android Studio and Android app development, but hadn’t really gotten to know it too well. It can be thought of as—
- Models for handling data and business logic
- Views for handling graphical user interface objects and presentation
- Controllers for handling the user interface and application
The tutorial I was following described MVC as an, “…architectural pattern, which enforces a separation between the data in the application (such as user information) and the code used to display it.”
Since I was trying to focus on high-level thinking, this high level view of the architecture of Rails applications stuck out as particularly important. This was exactly the type of thing that, if I was being lazy, I could probably skim over and not pay too much attention to. And without ever learning the importance of MVC, I could still probably manage to build Rails applications, but they would be messy. They wouldn’t work as well, would take longer to develop, and would be harder to maintain.
So I took my time, took notes, and I let the MVC knowledge settle in.
In Rails, they’re conveniently separated into files in separate directories.
├── app
│ ├── controllers
│ ├── models
│ └── views
│ └── (several other directories)
It would take some actual, hands-on development for me to get a practical understanding of what MVC meant, because abstract knowledge is often different from practical knowledge, but it was now something that I was aware of as I began programming.
Later on I would learn of the “skinny controller, fat model” mantra, and since I had already been in the MVC mindset, this actually made a lot of sense to me. I adjusted my development strategy accordingly. If I had ignored the high level architecture, I might have just thrown all of the logic into the controller. I wouldn’t have understood what was meant by “skinny controller, fat model”, my projects would have suffered, and perhaps worst of all, I could have embarrassed myself in front of other developers!
The Magic of Rails
This has been a topic of great discussion for as long as Rails has been around. Ruby on Rails is confusing to newcomers, and it has been described as, “too magical”. I, too, was confused at first. All over the application, there were references to functions or files that I couldn’t find definitions for.
Rails feels magical because it does a lot for you. It imports things, renders things, passes instance variables from the controller to the view, and more. Just adding a resource to the routes.rb
file (i.e., resources :users
), adds seven different routes for CRUD operations. There are the reusable partials, which are special types of views indicated by an underscore at the beginning of their filename. And don’t even get me started on the magic involved when has_secure_password
is added to a model.
It’s intimidating! People can, and do, say things like, “if you think Rails is magical, you just haven’t read the documentation.” Kudos to the people who can sit down, read, and understand documentation that easily, but I know that, for me, I had to experience it first hand before it began to make sense.
But with practice and research, the magic fades away. All of the implicitness, abstraction, and “magic” in Rails serves a purpose. It’s not just there to be confusing. After becoming comfortable, the payoff is that development is fast, time consuming tasks are lifted away, and the developer’s quality of life is high. Being able to utilize the magic of Rails becomes a sort of superpower.
That being said, there are potential downsides. Firstly, the steep learning curve is a barrier to entry for those looking to use Rails. There is also the risk, especially when it comes to metaprogramming, that code can become too complex and errors too hard to trace. Ruby on Rails is a framework where code can do the exact same thing but look so very different, which can make it hard to maintain a project, especially across a development team where multiple individuals are contributing.
Ruby
The Ruby language could have its own dedicated post, so I’ll limit myself to the three things that stuck out to me the most.
Everything is an object
This is not entirely true, but it’s mostly true. Ruby is extremely object-oriented. Of course there’s exceptions (like if
statements, or flow control keywords such as break
and continue
), but still, mostly everything is an object. And objects have classes. And classes have methods.
As a fun little experiment, we can inspect the nature of the number 1—
1.class
=> Integer
1.class.superclass
=> Numeric
1.class.superclass.superclass
=> Object
And finally, 1.class.superclass.superclass.superclass
=> BasicObject
The same can be done with nil
—
nil.class
=> NilClass
nil.class.superclass
=> Object
nil.class.superclass.superclass
=> BasicObject
So both the integer, 1, and the nil object, nil
, eventually inherit BasicObject
. And, as it goes, anything that is a subclass of BasicObject
has access to BasicObject
’s methods. I just think that is so cool. Everything has a common ancestor–there’s a sort of oneness to it all.
Return values
In Ruby, you can print to the console using puts “Hello World!”
. This alone isn’t very interesting, but if you inspect the behavior of puts
, it shines a light on the inner machinations of Ruby.
Calling puts
will print a string, but afterwards the method will return nil
.
Ruby is strange in this way, and it’s important for a developer to be very aware of this peculiarity. In Ruby, everything evaluates to a value. The puts
method must return something, so it returns nil
.
And in the methods that you’re writing, if there’s not a return call, what ends up being returned will be the last evaluated piece of code in the method.
This behavior was very different from other languages I’ve used before, and stuck out to me as very important—it’s a fundamental element of Ruby. Not understanding an aspect of a language such as this could cause lots of frustration and heartache further down the road.
Blocks
Blocks are one of the most confusing, yet most powerful elements of Ruby. They allow you to group expressions, save them to a variable, and even pass them to a method. I’m still getting used to blocks and realizing their usefulness, but overall I feel that they condense a lot of code, and also make code more reusable and extensible.
That being said, right now I usually just use blocks for simple iteration.
For example—
10.times { "Howdy y’all" }
Or iterating over an array—
[‘h’, ‘o’, ‘w’, ‘d’, ‘y’].each do |letter|
puts "#{letter} is in the block"
end
Takeaways—What I Really Like
I alluded to this earlier, but one of my favorite things about Ruby on Rails is the directory structure. It was overwhelming at first, but now it reveals itself as one of Rails greatest qualities. There’s a homely consistency across different Rails projects, which is a huge benefit for developers (especially us consultants), who are frequently hopping between different projects.
Another of my favorite aspects of Rails is the passionate community. From such a community stems solid documentation, community support, very good tutorials, and lots of online discussions. The community impact has also seeped into Rails through the form of coding conventions and development styles. These conventions are not required by any means, but they’re so ingrained in Rails as a framework that they can’t help but promote better applications and inspire better development practices. (A lot of the conventions can be found in this blog post, and they include the DRY principle, test-driven design, and utilizing data migrations.)
And finally, harnessing the “magic” of Rails has become such a joy during the development process. Developing in Rails now feels faster than any other framework I’ve used before.
Takeaways—What’s Not So Good
The learning curve. There was a significant learning curve. I did not like this. That being said, once I got over the hump, I appreciated almost everything that was originally difficult (in fact, most of which was difficult is now in the What I Really Like section above!).
Another thing that I keep finding difficult, which I talked briefly about in the Magic of Rails section, is that Ruby code can look so different depending on how you choose to write it. There’s optional parenthesis, the option to leave out return
statements, complex blocks, metaprogramming, and more. It’s easy in Ruby on Rails to get a little fancy, and the drawback is that it’s hard to maintain someone else’s code when you see that they got all fancy with things.
There are ways to remedy this problem—always aim to do things in the most simple way, use tools like RuboCop that enforce syntax rules, follow the Rails coding conventions, and you can maybe even go so far as to have standard coding conventions within a company.
Fast-forward
I’m now a couple months into using Ruby on Rails (thankfully I got the job).
Unsurprisingly, there was so still much more to learn about the framework than what I was able to learn over the course of 7 days. And I’m still learning every day.
But nowadays, although I don’t have to worry about the time-crunch of an approaching interview, or demonstrating to someone that I know how to learn, I still try to keep the same mindset that I had when I was originally learning Rails—I try to remain aware of what I’m learning, and I aim to get the most out of every learning experience.
Loved the article? Hated it? Didn’t even read it?
We’d love to hear from you.
Awesome story!
I was in a similar situation recently and decided to just learn Ruby, with no Rails, so I made this: https://github.com/trathborne/guillot
and wow, Ruby was such a joy to learn and use! I took another job, though, and now I’m discovering
https://www.opencypher.org/ with Python and wishing I could use http://neo4jrb.io/ instead.
Great article! Awesome high level overview of both Rails and Ruby. As somebody in a bootcamp going through the weeds on Rails, it’s really helpful to zoom out and see the big picture. Appreciate it!
great article dude. I had a similar experience a few years ago, only I did it before I had looked for work.
to this day rails is still my favorite mvc framework! can’t wait for static typing!
hahaha im in the exact same situation right now, except i get an extra week but also have to learn React instead of Vue (my prev exp). 10 days left. the grind game is real
Thank you so much for this post. Having just started at a company not knowing ruby or rails, I was feeling overwhelmed and a bit lost. I’m glad to hear that I am not alone in finding all the magic a bit scary at first and now I have hope that I just need to get past the learning curve to start enjoying as everyone else seems to!