SIMPLIFYING 
CODE 
MONSTER TO ELEGANT 
IN N<5 STEPS 
Tute Costa - @tutec
REFACTORING PATTERNS 
(And all these buzz words.)
ABOUT TUTE
20**: STUDENT/FREELANCER 
I learned CS theory at school 
I practiced at home different languages 
I built small projects as a freelancer 
Learned web dev alongside good mentors
2011/2012: CHEFSURFING 
We wrote so much code. 
Every 2 months complexity would bite us. 
Stop-the-world. Refactor. 
Not predictable. Not sustainable.
CHEF SURFING FELT LIKE
2013: GENERAL ASSEMBLY 
Rails app in BAD shape. But well tested. 
I was told “refactor allthethings!“ 
Improved productivity week after week
GENERAL ASSEMBLY FELT 
LIKE
2014: THOUGHTBOT 
Code Quality is daily bread and butter 
We heavily prioritize what to build 
Technical Debt is not allowed 
Makes for developers and clients' 
happiness
THOUGHTBOT FEELS LIKE
ANOMALY 1: CODE DOESN'T READ WELL 
Before: 
if hash[row[1]][date] != row[0] 
# ... 
After: 
if remove_duplicates?(row, date) 
# ... 
def remove_duplicates?(data, date)
INTENTION REVEALING METHOD 
1. Add comments if code needs it 
2. Transform comments into methods 
Transform them syntactically, then create the method. 
3. Comments are now code. 
And the code describes itself.
INTENTION REVEALING METHOD 
Comments are now code. 
And the code describes itself. 
MASSIVE 
SUCCESS.
INTENTION REVEALING METHOD 
It's arguably the easiest pattern. 
But the hardest as well.
ANOMALY 2: WHAT IS NIL? 
Hint: it's a troublemaker. 
Source of hard to spot errors: 
Undefined method `name' for nil 
session[:current_user] # => nil 
session[:current_dinozauh] # => nil 
if (false) then 1 end # => nil 
empty_method() # => nil
ANOMALY 2: WHAT IS NIL? 
A symbol is better than nil: 
# In Ruby: 
# 'a' || 'b' # => 'a' 
# nil || 'b' # => 'b' 
# false || 'b' # => 'b' 
def current_user 
User.find_by_id(id) || :guest_user 
end 
current_user.name 
undefined method `name' for 
:guest_user:Symbol
ANOMALY 2: SO MANY IFS! 
If there may be nil we need to enclose it 
with an if: 
if current_user 
"Hi #{current_user.name}!" 
else 
"Hi guest!" 
end
PATTERN: NULL OBJECTS 
Instead of nil, return a new object 
class NullUser 
def name 
'guest' 
end 
end 
def current_user 
User.find_by_id(id) || 
NullUser.new 
end 
"Ohai, #{current_user.name}!"
ANOMALY 3: GINORMOUS METHOD 
class ExportJob 
# Instance variables 
# Many other methods 
# And... 
def row_per_day_format(file_name) 
file = File.open file_name, 'r:ISO-8859-1' 
# hash[NivelConsistencia][date] = [[value, status]] 
hash = { '1' => {}, '2' => {} } 
dates = [] 
str = '' 
CSV.parse(file, col_sep: ';').each do |row| 
next if row.empty? 
next if row[0] =~ /^/// 
date = Date.parse(row[2]) 
(13..43).each do |i| 
measurement_date = date + (i-13) 
# If NumDiasDeChuva is empty it means no data 
value = row[7].nil? ? -99.9 : row[i] 
status = row[i + 31] 
hash_value = [value, status] 
dates << measurement_date 
hash[row[1]][measurement_date] = hash_value 
end 
end
REPLACE METHOD WITH METHOD OBJECT 
1/4. Create a class with same initialization 
arguments as BIG method 
class FormatAtoB 
def initialize(file_name) 
@file_name = file_name 
end 
end
REPLACE METHOD WITH METHOD OBJECT 
2/4. Copy & Paste the method's body in 
the new class, with no arguments 
class FormatAtoB 
def initialize(file_name) 
@file_name = file_name 
end 
def row_per_day_format 
file = File.open file_name, 'r:ISO-8859-# hash[NivelConsistencia][date] = [[value, hash = { '1' => {}, '2' => {} } 
dates = [] 
str = '' 
CSV.parse(file, col_sep: ';').each do |row|
REPLACE METHOD WITH METHOD OBJECT 
3/4. Replace original method with a call to 
the new class 
def row_per_day_format(file_name) 
FormatAtoB.new(file_name).row_per_day_format 
end
REPLACE METHOD WITH METHOD OBJECT 
4/4. Apply "Intention Revealing Method" to 
the class. Voilà. 
class FormatAtoB 
def initialize(file_name) 
@file_name = file_name 
end 
def row_per_day_format 
load_file_a 
format_data 
end 
private 
def load_file_a
REPLACE METHOD WITH METHOD OBJECT 
http://confreaks.com/videos/1071-cascadiaruby2012- 
therapeutic-refactoring
WE COVERED 
Intention Revealing Method 
Turns comments unnecessary. Code reads better. 
Null Objects 
Avoids nil objects and ifs. 
Replace Method with Method Object 
Refactor big methods at ease in a clean new container. 
QUESTIONS ABOUT THESE?
NEXT STEPS: "4 RULES" 
1. Classes of at most 100 lines of code 
2. Methods of at most 5 lines of code 
3. A method accepts at most 4 arguments 
4. A controller instantiates only one object
WHY REFACTORING 
Not only about aesthetics, but shared 
understanding, bug-chase, performance. 
We work with the tools with which we 
work. We are users and creators. 
If I have a bias I choose "over-engineering". 
"Under-engineering" is 
risky, expensive, and over-crowded.
EL FIN 
@tutec 
QUESTIONS ABOUT ANYTHING?

Simplifying Code: Monster to Elegant in 5 Steps

  • 1.
    SIMPLIFYING CODE MONSTERTO ELEGANT IN N<5 STEPS Tute Costa - @tutec
  • 2.
    REFACTORING PATTERNS (Andall these buzz words.)
  • 3.
  • 4.
    20**: STUDENT/FREELANCER Ilearned CS theory at school I practiced at home different languages I built small projects as a freelancer Learned web dev alongside good mentors
  • 5.
    2011/2012: CHEFSURFING Wewrote so much code. Every 2 months complexity would bite us. Stop-the-world. Refactor. Not predictable. Not sustainable.
  • 6.
  • 7.
    2013: GENERAL ASSEMBLY Rails app in BAD shape. But well tested. I was told “refactor allthethings!“ Improved productivity week after week
  • 8.
  • 9.
    2014: THOUGHTBOT CodeQuality is daily bread and butter We heavily prioritize what to build Technical Debt is not allowed Makes for developers and clients' happiness
  • 10.
  • 11.
    ANOMALY 1: CODEDOESN'T READ WELL Before: if hash[row[1]][date] != row[0] # ... After: if remove_duplicates?(row, date) # ... def remove_duplicates?(data, date)
  • 12.
    INTENTION REVEALING METHOD 1. Add comments if code needs it 2. Transform comments into methods Transform them syntactically, then create the method. 3. Comments are now code. And the code describes itself.
  • 13.
    INTENTION REVEALING METHOD Comments are now code. And the code describes itself. MASSIVE SUCCESS.
  • 14.
    INTENTION REVEALING METHOD It's arguably the easiest pattern. But the hardest as well.
  • 15.
    ANOMALY 2: WHATIS NIL? Hint: it's a troublemaker. Source of hard to spot errors: Undefined method `name' for nil session[:current_user] # => nil session[:current_dinozauh] # => nil if (false) then 1 end # => nil empty_method() # => nil
  • 16.
    ANOMALY 2: WHATIS NIL? A symbol is better than nil: # In Ruby: # 'a' || 'b' # => 'a' # nil || 'b' # => 'b' # false || 'b' # => 'b' def current_user User.find_by_id(id) || :guest_user end current_user.name undefined method `name' for :guest_user:Symbol
  • 17.
    ANOMALY 2: SOMANY IFS! If there may be nil we need to enclose it with an if: if current_user "Hi #{current_user.name}!" else "Hi guest!" end
  • 18.
    PATTERN: NULL OBJECTS Instead of nil, return a new object class NullUser def name 'guest' end end def current_user User.find_by_id(id) || NullUser.new end "Ohai, #{current_user.name}!"
  • 19.
    ANOMALY 3: GINORMOUSMETHOD class ExportJob # Instance variables # Many other methods # And... def row_per_day_format(file_name) file = File.open file_name, 'r:ISO-8859-1' # hash[NivelConsistencia][date] = [[value, status]] hash = { '1' => {}, '2' => {} } dates = [] str = '' CSV.parse(file, col_sep: ';').each do |row| next if row.empty? next if row[0] =~ /^/// date = Date.parse(row[2]) (13..43).each do |i| measurement_date = date + (i-13) # If NumDiasDeChuva is empty it means no data value = row[7].nil? ? -99.9 : row[i] status = row[i + 31] hash_value = [value, status] dates << measurement_date hash[row[1]][measurement_date] = hash_value end end
  • 20.
    REPLACE METHOD WITHMETHOD OBJECT 1/4. Create a class with same initialization arguments as BIG method class FormatAtoB def initialize(file_name) @file_name = file_name end end
  • 21.
    REPLACE METHOD WITHMETHOD OBJECT 2/4. Copy & Paste the method's body in the new class, with no arguments class FormatAtoB def initialize(file_name) @file_name = file_name end def row_per_day_format file = File.open file_name, 'r:ISO-8859-# hash[NivelConsistencia][date] = [[value, hash = { '1' => {}, '2' => {} } dates = [] str = '' CSV.parse(file, col_sep: ';').each do |row|
  • 22.
    REPLACE METHOD WITHMETHOD OBJECT 3/4. Replace original method with a call to the new class def row_per_day_format(file_name) FormatAtoB.new(file_name).row_per_day_format end
  • 23.
    REPLACE METHOD WITHMETHOD OBJECT 4/4. Apply "Intention Revealing Method" to the class. Voilà. class FormatAtoB def initialize(file_name) @file_name = file_name end def row_per_day_format load_file_a format_data end private def load_file_a
  • 24.
    REPLACE METHOD WITHMETHOD OBJECT http://confreaks.com/videos/1071-cascadiaruby2012- therapeutic-refactoring
  • 25.
    WE COVERED IntentionRevealing Method Turns comments unnecessary. Code reads better. Null Objects Avoids nil objects and ifs. Replace Method with Method Object Refactor big methods at ease in a clean new container. QUESTIONS ABOUT THESE?
  • 26.
    NEXT STEPS: "4RULES" 1. Classes of at most 100 lines of code 2. Methods of at most 5 lines of code 3. A method accepts at most 4 arguments 4. A controller instantiates only one object
  • 27.
    WHY REFACTORING Notonly about aesthetics, but shared understanding, bug-chase, performance. We work with the tools with which we work. We are users and creators. If I have a bias I choose "over-engineering". "Under-engineering" is risky, expensive, and over-crowded.
  • 28.
    EL FIN @tutec QUESTIONS ABOUT ANYTHING?