Simplifying Code: Monster to Elegant in 5 Steps

28
SIMPLIFYING CODE MONSTER TO ELEGANT IN N<5 STEPS Tute Costa - @tutec

Transcript of Simplifying Code: Monster to Elegant in 5 Steps

SIMPLIFYINGCODE

MONSTER TO ELEGANTIN N<5 STEPS

Tute Costa - @tutec

REFACTORING PATTERNS(And all these buzz words.)

ABOUT TUTE

20**: STUDENT/FREELANCERI learned CS theory at school

I practiced at home different languagesI built small projects as a freelancer

Learned web dev alongside good mentors

2011/2012: CHEFSURFINGWe 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 ASSEMBLYRails app in BAD shape. But well tested.I was told “refactor allthethings!“Improved productivity week after week

GENERAL ASSEMBLY FELTLIKE

2014: THOUGHTBOTCode Quality is daily bread and butterWe heavily prioritize what to buildTechnical Debt is not allowedMakes for developers and clients'happiness

THOUGHTBOT FEELS LIKE

Before:

After:

ANOMALY 1: CODE DOESN'T READ WELL

if hash[row[1]][date] != row[0] # ...

if remove_duplicates?(row, date) # ...

def remove_duplicates?(data, date)

INTENTION REVEALING METHOD

1. Add comments if code needs it2. 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.

MASSIVESUCCESS.

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 nilsession[:current_user] # => nilsession[:current_dinozauh] # => nilif (false) then 1 end # => nilempty_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_userend

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 OBJECTSInstead of nil, return a new objectclass NullUser def name 'guest' endend

def current_user User.find_by_id(id) || NullUser.newend

"Ohai, #{current_user.name}!"

ANOMALY 3: GINORMOUS METHODclass 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 initializationarguments as BIG method

class FormatAtoB def initialize(file_name) @file_name = file_name endend

REPLACE METHOD WITH METHOD OBJECT

2/4. Copy & Paste the method's body inthe 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-1' # hash[NivelConsistencia][date] = [[value, status]] 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 tothe new class

def row_per_day_format(file_name) FormatAtoB.new(file_name).row_per_day_formatend

REPLACE METHOD WITH METHOD OBJECT

4/4. Apply "Intention Revealing Method" tothe 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 MethodTurns comments unnecessary. Code reads better.

Null ObjectsAvoids nil objects and ifs.

Replace Method with Method ObjectRefactor big methods at ease in a clean new container.

QUESTIONS ABOUT THESE?

NEXT STEPS: " "4 RULES1. Classes of at most 100 lines of code2. Methods of at most 5 lines of code3. A method accepts at most 4 arguments4. A controller instantiates only one object

WHY REFACTORINGNot only about aesthetics, but sharedunderstanding, bug-chase, performance.We work with the tools with which wework. We are users and creators.If I have a bias I choose "over-engineering". "Under-engineering" isrisky, expensive, and over-crowded.

EL FIN

@tutec

QUESTIONS ABOUT ANYTHING?