If you are looking to become a better programmer, one of the most frequent pieces of advice is to “read code”. What is often meant by this is that you should explore and try to understand open-source projects, preferably successful and well-maintained ones. The thought being that you will pick up new ways of solving problems, structuring code, architecting apps, the list goes on. Moreover, reading code is a required skill whenever you switch jobs or want to contribute to a open-source project.
So you’re convinced you need to read some code. You crack open your browser,
go to github repo for the app or framework that is super popular in your
programming language of choice.
Yes this isn't quite a fair example, the rails repo has several gems/packages within it, and yes you can always find smaller repos. You’re confronted by a repo with 59,000 commits, 12 directories, and 16 top level files. “No big deal”, you say, “most of that is config files and tests” You can separate the wheat from the chaff. You begin merrily clicking around, and trying to figure out whats going. Cut to 20 minutes later, you’re quite lost and you desparately need to find out what is going on reddit/hackernews/twitter/digg/myspace/digg/something-else. It’s not really clear where things “start” and where the file you are on really fits in to the “big picture”.
Reading code is a skill and I’m convinced of its value from experience. For a newer developer though, the advice of “read code” is often easier said than done. Moreover, it is a skill that must be used more frequently with your level of seniority. In a well run shop, code reviews should be taking place. Likely as you become more senior, review will occupy more and more of your time. Therefore, as an exercise I wanted to perform a code-reading and detail my thoughts as I went along. I’ll focus on both the process and the actual code.
For the exercise, I chose Piotr Solnic’s Ruby Object Mapper library: rom-rb. ROM is actually a set of libraries (gems), focused on persisting data often it is used with databases but it is not confined to that . I chose this set of repos not so much because I was familiar with it, rather the opposite, I don’t use it in any project. However there were several things that piqued my interest:
- I have been casually following the project and Piotr’s annoucements since hearing him on Ruby Rogues
- I’ve read several articles recently on the topic that have piqued my interest
- ROM and its related libraries (dry-rb) bill themselves as a new approach to developing web apps in ruby. As such, I expect to see new concepts in these repos. “Right” or “Wrong” these new concepts should provide a new way of looking problems.
This set of blog posts will focus on this ruby library, and therefore will hopefully be helpful to ruby developers. However, I will also be talking about general code reading techniques, so non-rubyists should find value as well.
I intend to cover at least these topics in future posts:
- Project Idioms
- Tips and Techniques for easier and more productive code reading
For now, to whet your appetite I will detail my setup process for code reading.
How do we start code reading? Obviously, we could click around through
the rom repositores on github. I generally only go this route, when
I need to look at something quickly when reading another repo or the code I am
using is poorly documented. There is far too much clicking around and
back-paging for my taste. If I am going to be looking at a set of code
frequently, I will clone the repo into my
~/temp directory. At that point, I
can open up the directory in my editor and freely navigate between files using
fuzzy file finder or grepping/searching the repo.
However, since we are working in a ruby library crossing multiple repos/gems,
let’s try a slightly different approach. Namely, lets download allow the gems
bundler and also create a test script so that we can drop a
debugger in the code.
First we make a new directory called ruby-code-reading and initialize bundler in that directory.
mkdir ruby-code-reading cd ruby-coding-reading bundler init .
Next, we open the Gemfile and add the our gem(s) of interest and our favorite debugger:
# frozen_string_literal: true # A sample Gemfile source "https://rubygems.org" gem 'rom', '>= 2.0.0' gem 'rom-sql' gem 'sqlite3' gem 'rom-repository' gem 'pry-byebug'
Finally, we can store the gems within this directory by passing a path during bundle install:
bundle install --path=vendor
This gives us all of the gems we are interested in:
$ tree -L 5 -d . └── vendor └── ruby └── 2.3.0 └── gems ├── byebug-9.0.6 ├── coderay-1.1.1 ├── concurrent-ruby-1.0.4 ├── dry-configurable-0.5.0 ├── dry-container-0.6.0 ├── dry-core-0.2.4 ├── dry-equalizer-0.2.0 ├── dry-initializer-0.11.0 ├── dry-logic-0.4.1 ├── dry-struct-0.1.1 ├── dry-types-0.9.4 ├── ice_nine-0.11.2 ├── inflecto-0.0.2 ├── method_source-0.8.2 ├── pry-0.10.4 ├── pry-byebug-3.4.2 ├── rom-3.0.1 ├── rom-mapper-0.5.0 ├── rom-repository-1.0.1 ├── rom-sql-1.0.0 ├── sequel-4.43.0 ├── slop-3.6.0 ├── sqlite3-1.3.13 └── transproc-1.0.0
At this point we simply add a small test script in the directory, so that
when you are looking at code you don’t think you quite understand you can drop a
require pry; binding.pry and then write some code that will get you to that
point through the test script.
This only took about 3-4 minutes to setup but now I can easily fuzzy-find / grep/ or even inspect in the REPL any of the code that I am reading. We are ready to start orienting ourselves to ROM, stay tuned!
The series continues in the next post on orientation