Your SlideShare is downloading. ×
0
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Simplifying code  monster to elegant in n 5 steps
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Simplifying code monster to elegant in n 5 steps

1,456

Published on

In this workshop we'll learn how to transform complex, highly coupled code into a simpler, more readable and maintainable shape. We'll target known software anomalies with Refactoring Patterns‎, …

In this workshop we'll learn how to transform complex, highly coupled code into a simpler, more readable and maintainable shape. We'll target known software anomalies with Refactoring Patterns‎, following steps with a confined scope, assuring that we stay distant from "changed everything" commits while achieving quick design improvements.

We'll talk different solutions for Fat Models, God Objects, long method chains, NoMethodError on nils, long methods, bad naming and cold coffee.

Slides presented in RailsConf 2014.

Published in: Engineering, Software, Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,456
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
31
Comments
0
Likes
3
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. SIMPLIFYING CODE MONSTER TO ELEGANT IN N<5 STEPS Tute Costa - @tutec
  • 2. WE'LL LEARN HOW TO TRANSFORM THIS // fax if (!empty($this->post['fax'])) { $site->fax = $this->post['fax'][0]; } // Facebook & Twitter if ($this->post['social_media']) { if (!empty($this->post['facebook'])) { $facebookURL = $this->post['facebook']; if (strpos($this->post['facebook'], 'http://') === fal $facebookURL = "http://" . $this->post['facebook']; } $site->facebook_url = $facebookURL; } if (!empty($this->post['twitter'])) { $twitterURL = $this->post['twitter']; if (strpos($this->post['twitter'], 'http://') === fals
  • 3. Into THIS current_user .public_profiles .update(params)
  • 4. ABOUT YOU
  • 5. ABOUT TUTE
  • 6. ABOUT TUTE
  • 7. ABOUT TUTE
  • 8. ABOUT TUTE
  • 9. ABOUT TUTE Alright if we'll go all technical I'll say "bug".
  • 10. 2011/2012: CHEFSURFING We wrote so much code. Every 2 months complexity would bite us. Stop-the-world. Test && Refactor. Not predictable. Not sustainable.
  • 11. 2013: GENERAL ASSEMBLY Rails app in BAD shape. But well tested. I was told “refactor allthethings!“ Improved productivity week after week I want to repeat (and improve!) this story.
  • 12. PRECONDITION: THERE'S TESTS
  • 13. REFACTORING PATTERNS 1. Intention Revealing Method 2. Special Case Object 3. Replace Method with Method Object 4. Service Object
  • 14. INTENTION REVEALING METHOD Why it's called is more important than how/what it does. # Remove duplicates? if hash[row[1]][date] != row[0] # ... if remove_duplicates?(row, date) # ... def remove_duplicates?(data, date)
  • 15. 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. Code describes itself.
  • 16. INTENTION REVEALING METHOD git clone http://github.com/tute/ refactoring-workshop
  • 17. INTENTION REVEALING METHOD <5 lines per method No problem!
  • 18. INTENTION REVEALING METHOD It's arguably the easiest pattern. But the hardest as well.
  • 19. INTENTION REVEALING METHOD RESOURCES http://ntcoding.blogspot.com.ar/2011/05/clea code-tricks-intention-revealing.html http://www.codinghorror.com/blog/2008/07/ without-comments.html http://c2.com/cgi/wiki?ExtractMethod
  • 20. 2. SPECIAL CASE OBJECTS No more ifs or trys.
  • 21. 2. SPECIAL CASE OBJECTS nil is a troublemaker. Source of hard to trace exceptions: Undefined method `email' for nil:NilClass session[:current_user] # => nil if (false) then 1 end # => nil empty_method() # => nil
  • 22. 2. SPECIAL CASE OBJECTS A symbol is better than nil: def current_user User.find_by_id(params[:id]) || :guest_user end current_user.email undefined method `email' for :guest_user:Symbol
  • 23. 2. SPECIAL CASE OBJECTS If there may be nilwe need to enclose it with an if: if current_user "Ohai, #{current_user.email}!" else 'Ohai, guest!' end
  • 24. 2. SPECIAL CASE OBJECTS Instead of nil, return a new object class NullUser def email 'guest' end end def current_user User.find(session[:user_id]) || NullUser.new end "Ohai, #{current_user.email}!"
  • 25. 2. SPECIAL CASE OBJECTS RESOURCES RubyTapas #112 http://devblog.avdi.org/2011/05/30/null-objec falsiness/ http://martinfowler.com/eaaCatalog/specialC http://www.cs.oberlin.edu/~jwalker/nullObjPa http://nickknowlson.com/blog/2013/04/16/w maybe-is-better-than-null/
  • 26. 3. REPLACE METHOD WITH METHOD OBJECT How to refactor a GIGANTIC method without getting lost in the cold night. defrow_per_day_format(file_name) file=File.openfile_name,'r:ISO-8859-1' #hash[NivelConsistencia][date]=[[value,status]] hash={'1'=>{},'2'=>{}} dates=[] str='' CSV.parse(file,col_sep:';').eachdo|row| nextifrow.empty? nextifrow[0]=~/^/// date=Date.parse(row[2]) (13..43).eachdo|i| measurement_date=date+(i-13) #IfNumDiasDeChuvaisemptyitmeansnodata 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
  • 27. 3. REPLACE METHOD WITH METHOD OBJECT 1/5. Look carefully at the code. Get scared. defrow_per_day_format(file_name) file=File.openfile_name,'r:ISO-8859-1' #hash[NivelConsistencia][date]=[[value,status]] hash={'1'=>{},'2'=>{}} dates=[] str='' CSV.parse(file,col_sep:';').eachdo|row| nextifrow.empty? nextifrow[0]=~/^/// date=Date.parse(row[2]) (13..43).eachdo|i| measurement_date=date+(i-13) #IfNumDiasDeChuvaisemptyitmeansnodata 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
  • 28. 3. 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
  • 29. 3. 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-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|
  • 30. 3. 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
  • 31. 3. 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 # [...] end end
  • 32. 3. REPLACE METHOD WITH METHOD OBJECT RESOURCES http://www.refactoring.com/catalog/replaceM http://confreaks.com/videos/1071-cascadiaru refactoring
  • 33. 4. SERVICE OBJECTS Decoupling different concerns from chubby classes
  • 34. 4. SERVICE OBJECTS IF WE ADD NEW FUNCTIONALITY TO AN OBJECT: It couples to a new dependency It loses cohesion Testing gets harder and slower We can't describe the model without connectors "and"/"or". SRP.
  • 35. 4. SERVICE OBJECTS If it's a domain concept, it's an Object. If it's only an algorithm (no state) we call it a Service.
  • 36. 4. SERVICE OBJECTS RESOURCES http://blog.codeclimate.com/blog/2012/10/17 ways-to-decompose-fat-activerecord- models/#service-objects http://railscasts.com/episodes/398-service- objects
  • 37. NEXT STEPS Send Pull Requests to GitHub. It's a gold mine: sferik/rails_admin Code Climate TracksApp/tracks Code Climate
  • 38. 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
  • 39. WHY REFACTORING Not only about aesthetics, but shared understanding, 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.
  • 40. - EL FIN @tutec github.com/tute

×