Approaching Rails Legacy Systems Chapter 3: Where to start?

Reading Time: 5 minutes

Well, where to start? If you are reading this article, you must have read Chapter 1 and Chapter 2 of this Approaching Rails Legacy Systems series by now. In this one, we're going to get into our codebase and do some analysis and auditing to know how things are in there. We're going to use the DRY principle and some statistics analysis tools that will let you discover some things about it.

 

Don’t Repeat Yourself: DRY

Don't repeat yourself (DRY) is a principle of software development aimed at reducing repetition of software pattern. It does it replacing it with abstractions or using data normalization to avoid redundancy.

Tour time! Lookup for repeated code and review your views, you might find a lot of duplicated code in there. That can be easily extracted to partials, but this also applies to controllers, models, and basically everywhere.

Static Analysis Tools

There are a lot of static analysis tools that you can use and will help to improve your application, following the daily boy scout rule.

Let's take a look at only a few of its tools:

Churn

Code churn is a measure of the change in our code:

High method and class churn have been shown to have increased bug and error rates.

In this specific example, look at how the file lib/churn/churn_calculator.rb is the one that has been changed most often. That should give us some idea about its complexity, and should suggest it is one of the files that have a bigger impact or major error rates on our application.

Reek

Reek is a tool that examines Ruby classes, modules and methods, and reports any code smells it finds.

$  reek --format json app/models/advertising.rb

[  
   {  
      "context":"Advertising",
      "lines":[  
         1
      ],
      "message":"has no descriptive comment",
      "smell_type":"IrresponsibleModule",
      "source":"app/models/advertising.rb",
      "documentation_link":"https://github.com/troessner/reek/blob/v5.3.1/docs/Irresponsible-Module.md"
   }
]

You could quickly look for code smells in a bunch of files, just to see how stinky the code base is:

$ reek --format json app/models/ | jq '.[] .smell_type' | sort | uniq
"IrresponsibleModule"
"FeatureEnvy"
"InstanceVariableAssumption"
"UtilityFunction"

code_metrics

rake stats is great for looking at statistics on your code, displaying things like KLOCs (thousands of lines of code), and your code-to-test ratio.

$ be rake stats
+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          | 11892 |  9621 |      93 |     873 |   9 |     9 |
| Helpers              |  2695 |  2192 |       0 |     281 |   0 |     5 |
| Models               | 11928 |  9453 |     179 |    1075 |   6 |     6 |
| Mailers              |   324 |   255 |       5 |      26 |   5 |     7 |
| Javascripts          |  8096 |  6606 |       1 |     932 | 932 |     5 |
| Libraries            |  4769 |  3679 |      43 |     333 |   7 |     9 |
| Controller specs     |  1812 |  1438 |       0 |      18 |   0 |    77 |
| Helper specs         |   129 |   109 |       0 |       2 |   0 |    52 |
| Lib specs            |  1269 |   980 |       1 |      17 |  17 |    55 |
| Mailer specs         |    26 |    11 |       0 |       0 |   0 |     0 |
| Model specs          |  2380 |  1917 |       0 |       3 |   0 |   637 |
| Presenter specs      |    26 |    24 |       0 |       0 |   0 |     0 |
| Serializer specs     |   154 |   119 |       0 |       0 |   0 |     0 |
| Service specs        |  3092 |  2361 |       0 |       5 |   0 |   470 |
| Worker specs         |   199 |   142 |       0 |       1 |   0 |   140 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                | 48791 | 38907 |     322 |    3566 |  11 |     8 |
+----------------------+-------+-------+---------+---------+-----+-------+
  Code LOC: 31806     Test LOC: 7101     Code to Test Ratio: 1:0.2
$ code_metrics
+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          | 11892 |  9621 |      93 |     873 |   9 |     9 |
| Helpers              |  2695 |  2192 |       0 |     281 |   0 |     5 |
| Models               | 11928 |  9453 |     179 |    1075 |   6 |     6 |
| Mailers              |   324 |   255 |       5 |      26 |   5 |     7 |
| Javascripts          |  8096 |  6606 |       1 |     932 | 932 |     5 |
| Libraries            |  4769 |  3679 |      43 |     333 |   7 |     9 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                | 39704 | 31806 |     321 |    3520 |  10 |     7 |
+----------------------+-------+-------+---------+---------+-----+-------+
  Code LOC: 31806     Test LOC: 0     Code to Test Ratio: 1:0.0

Rubocop

RuboCop is a Ruby static code analyzer. Out of the box, it will enforce many of the guidelines outlined in the community’s Ruby Style Guide.

$ rubocop --format json app/models > rubocop.json
$ cat rubocop.json
{  
   "metadata":{  
      "rubocop_version":"0.64.0",
      "ruby_engine":"ruby",
      "ruby_version":"2.5.1",
      "ruby_patchlevel":"57",
      "ruby_platform":"x86_64-darwin15"
   },
   "files":[  
      {  
         "path":"app/models/advertising.rb",
         "offenses":[  
            {  
               "severity":"convention",
               "message":"Style/Documentation: Missing top-level class documentation comment.",
               "cop_name":"Style/Documentation",
               "corrected":false,
               "location":{  
                  "start_line":1,
                  "start_column":1,
                  "last_line":1,
                  "last_column":5,
                  "length":5,
                  "line":1,
                  "column":1
               }
            },
            {  
               "severity":"convention",
               "message":"Style/FrozenStringLiteralComment: Missing magic comment `# frozen_string_literal: true`.",
               "cop_name":"Style/FrozenStringLiteralComment",
               "corrected":false,
               "location":{  
                  "start_line":1,
                  "start_column":1,
                  "last_line":1,
                  "last_column":1,
                  "length":1,
                  "line":1,
                  "column":1
               }
            }
         ]
      },
     ,
      {  
         "path":"app/models/user.rb",
         "offenses":[  
            {  
               "severity":"convention",
               "message":"Style/Documentation: Missing top-level class documentation comment.",
               "cop_name":"Style/Documentation",
               "corrected":false,
               "location":{  
                  "start_line":1,
                  "start_column":1,
                  "last_line":1,
                  "last_column":5,
                  "length":5,
                  "line":1,
                  "column":1
               }
            },
            {  
               "severity":"convention",
               "message":"Style/FrozenStringLiteralComment: Missing magic comment `# frozen_string_literal: true`.",
               "cop_name":"Style/FrozenStringLiteralComment",
               "corrected":false,
               "location":{  
                  "start_line":1,
                  "start_column":1,
                  "last_line":1,
                  "last_column":1,
                  "length":1,
                  "line":1,
                  "column":1
               }
            }
         ]
      }
   ],
   "summary":{  
      "offense_count":11,
      "target_file_count":5,
      "inspected_file_count":5
   }
}

We can find all the severities and try to prevent in the future to add more than the ones we already cleaned up:

$ cat rubocop.json | jq '.files[].offenses[].severity' | sort | uniq -c
   7380 "convention"
    315 "warning"

Unused

A command line tool to identify unused code.

RubyCritic

RubyCritic is a gem that wraps around static analysis gems such as Reek, Flay and Flog to provide a quality report of your Ruby code.

You can use RubyCritic to get an intuitive dashboard like this one:

rubycritics

I hope you find this blogpost compelling. In the following chapter, we are going to go over the last piece of advice on this series of blog posts.

Thanks for reading!

0 Shares:
You May Also Like
Read More

Active Job

Reading Time: 3 minutes Hey guys! I hope you are doing great. Recently I worked on a Spree extension to index products…