This is the chapter 1 of a series of blog posts about some pieces of advice, tools, and tips you can consider when you arrive to a Rails project considered as Legacy.
Imagine that you landed in a “new” project (new for you, but itβs actually an 8-year-old project). Suddenly you find yourself into a lot of spaghetti code and a monolithic application (which is not bad at all). You can also tell that a small battalion of software engineers has marched through the project.
Has something like this happened to you before? Does it sound familiar?
No worries. In this Approaching Rails Legacy Systems series I’m going to give you hints for when you arrive in a Rails project that can be considered as Legacy.
In this chapter, we take a look at the Project Anatomy. So, it’s important for you to understand your project structure, type of files, and more.
Ready?
What’s a Legacy System?
Wikipedia definition:
In computing, a legacy system is an old method, technology, computer system, or application program, “of, relating to, or being a previous or outdated computer system,” yet still in use. Often referencing a system as “legacy” means that it paved the way for the standards that would follow it. This can also imply that the system is out of date or in need of replacement.
Project Anatomy
Check your domain knowledge
Use ls
to look for clues on what the domain knowledge looks like:
$ ls
Brewfile Rakefile engines
Capfile app lib
Dockerfile bin log
ERP_DEPLOY_RUNBOOK.md circle.yml package.json
Gemfile client public
Gemfile.lock config script
Guardfile config.ru spec
Procfile db tmp
Procfile_no_npm doc vendor
README.md dump.rdb webpack.config.babel.js
As you can see above, we found a Gemfile
. Did you see it? Awesome! Looks like this is a Rails Application, right? Well, then you can see that there is a spec
folder.
Woohoo! There might even be something to test the project. At this point, we are getting some good signs about this “new” app. So. it’s time to take a look inside our app
folder and see which other patterns we need to take into consideration or worry about in the future.
$ ls app/
assets mailers pdfs serializers views
controllers models presenters services workers
helpers overrides repositories uploaders
Oh, I see!, presenters
, services
, serializers
, uploaders
, repositories
, overrides
, so interesting. More things to consider π€π€. I expect you to be taking notes already.
presenter – acts upon the model and the view. It retrieves data from repositories (the model) and formats it for display in the view.
services – service objects that talks to external entities.
Martin Fowler describes a repository as follows:
repositories – A repository performs the tasks of an intermediary between the domain model layers and data mapping, acting in a similar way to a set of domain objects in memory. Client objects declaratively build queries and send them to the repositories for answers. Conceptually, a repository encapsulates a set of objects stored in the database and operations that can be performed on them, providing a way that is closer to the persistence layer. Repositories, also, support the purpose of separating, clearly and in one direction, the dependency between the work domain and the data allocation or mapping.
uploaders – π± Something weβll need to take care in the future.
overrides – π¨ Another pattern that weβll have to consider later in the future.
It feels like we understand our project better now π€, right?
How much of what?
Use ag
to have a quick search and wc
(short for word count) to check how much Ruby code do we have.
So far it looks like Rails and Ruby are the core of this app, but how much Ruby? Let’s see:
$ ag -l 'class|module' --ruby | wc
1873 1873 96753
How much Ruby in our lib
and app
?:
$ ag -l 'class|module' --ruby app lib |wc
1104 1104 48996
Wait a minute, why is this important? Details matter, right? But also the big picture is important, so I think is adequate to have in mind this kind of information to have a general idea of the size of your project.
What else?
Now, let’s see. How many file types are there?
$ find . -type f \
> | sed -e 's/.*\.//' \
> | sed -e 's/.*\///' \
> | sort | uniq -c | sort -rn| wc
1768 3536 74117
Amazing, so many files to deal with! π Let’s use find
, sort
, sed
, uniq
, head
to get the top counts per file type:
$ find . -type f \
> | sed -e 's/.*\.//' \
> | sed -e 's/.*\///' \
> | sort | uniq -c | sort -rn
4687 cache
1544 rb
593 erb
477 png
440 jpg
199 js
180 scss
145 html
124 deface
72 yml
59 svg
53 rake
46 coffee
36 gif
13 pack
13 idx
11 ttf
11 rabl
11 csv
11 css
10 sample
10 prawn
10 ejs
9 woff
9 markdown
...
There are 1544 rb
files. π«π₯ And if we look closely, there are 477 png
files… Maybe, just maybe, we could consider using an S3 bucket and CDN to solve this. π» Remember, performance is king!!!
Project structure
Let’s use tree
to look deeper:
$ tree lib/
lib/
βββ 3d_coupon_updater.rb
βββ 3d_importer.rb
βββ assets
βββ data_warehouse
βΒ Β βββ order_field_converter.rb
βββ devise
βΒ Β βββ custom_failure.rb
βββ errors_app.rb
βββ google_tag_manager_data_layer.rb
βββ kmts.rb
14 directories, 109 files
The Rails guide says:
app/ – Contains the controllers, models, views, helpers, mailers, channels, jobs and assets for your application. You’ll focus on this folder for the remainder of this guide.
lib/ – Extended modules for your application.
Understand your boundaries
One way to help you understand the architecture is to understand the boundaries. Let’s start with your Gemfile
$ cat Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 4.0.13'
group :assets do
gem 'sass-rails'
gem 'coffee-rails', '~> 4.0.1' # TODO: remove all .coffee + this gem
gem 'uglifier', '~> 2.5.0'
end
# Assets
gem 'active_model_serializers', '~> 0.9.0.alpha1' # 0.10 introduces breaking changes
gem 'autoprefixer-rails', '~> 6.0.3'
gem 'bootstrap-sass'
gem 'bourbon', '~> 3.2.1'
gem 'fancybox2-rails', '~> 0.2.8'
gem 'mustache', '~> 0.99.5'
gem 'sass-mediaqueries-rails'
gem 'ui-rails'
gem 'paper_trail', '~> 4.0.0'
gem 'css_splitter', github: 'madebymade/css_splitter', branch: 'use-railtie'
gem 'route_downcaser'
gem 'stringjs-rails', '~> 2.1.0' # small util, mostly for unescapeHTML in cards
gem 'jbuilder' # what is this can we remove?
gem 'ahoy_matey'
gem 'kmts', '~> 2.0.0'
gem 'activeuuid', '~> 0.5.0'
gem 'ga_cookie_parser'
gem 'google-tag-manager-rails'
gem 'savon', '~> 2.11.1'
gem 'netsuite', '~> 0.4.0'
gem 'sift', '~> 1.1.7'
I recommend checking every dependency you don’t know much about, look at the documentation of those you don’t understand what the hell are doing, and at this point you should have the big picture of your boundaries. Remember your notes.
- Draw how the boundaries look like. Yes, take your notebook and draw:
- You don’t need to follow a specific methodology, just draw.
Let’s try Rubrowser
Rubrowser is a visualizer for ruby code (rails or otherwise), it analyze your code and extract the modules definitions and used classes/modules and render all these information as a directed force graph using D3. The idea is that the project opens every .rb file and parse it with parser gem then list all modules and classes definitions, and all constants that are listed inside this module/class and link them together.
There are a lot of tools that I enjoy using it. For example, letβs try rails-erd
This creates a quick entity-relationship diagram of your models, but you can also use the database manager of your choice.
So far we have looked at the anatomy of this project, but we still need to go far and beyond!
I hope you find this blogpost useful somehow. In the following chapter, we are going to use GIT as a tool to approach legacy systems and a couple of tools around this distributed version-control system that you might not be aware of.
Thanks for reading!
Oh, before saying goodbye, please share this on Twitter so your peers will know about this Approaching Rails Legacy Systems series:
Take a peek at some pieces of advice, tools, and tips you can consider when you arrive to a Rails project considered as Legacy. Share on X
@VΓctorVelΓ‘zquez, Director Of Engineering at MagmaLabs