Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Code Stinkers Anonymous

Avatar for Mark Cornick Mark Cornick
February 27, 2012

Code Stinkers Anonymous

2012 NOTE: Some links in this presentation are no longer valid. Sorry about that.

As a professional developer, especially one who works in Ruby, you hear about code quality all the time. You learn that testing your code and making it easy to maintain are the path to success. You know about TDD, BDD, TATFT and LMNOP. You learn to cycle from red to green to refactoring. We all do our best to write quality, maintainable, reusable code. We’re all human, though; some of us slip, and some of us have had to work hard at preventing code smells. In this talk, I’ll talk about how I learned to program, how going pro exposed flaws in my coding style, and how I’m working to improve my code quality, sharing some of my old stinky code, the better, refactored versions, and the lessons I’ve learned in honing my craft.

Avatar for Mark Cornick

Mark Cornick

February 27, 2012
Tweet

More Decks by Mark Cornick

Other Decks in Programming

Transcript

  1. A very Brief Introduction To Edsger W. Dijkstra who wi"

    be quoted a few times in the next few slides Friday, June 12, 2009
  2. Edsger W. Dijkstra (1930-2002) Notable for: • Shortest path algorithm

    • Reverse Polish notation • Being a curmudgeon Friday, June 12, 2009
  3. My Back Pages Or, “I was so much lamer then;

    I’m less lame than that now” Friday, June 12, 2009
  4. 10 TEXT:HOME 20 GOSUB 100 30 END 100 ?“YES I

    JUST GOSUBBED FOR NO REASON WHATSOEVER” 110 RETURN Friday, June 12, 2009
  5. “It is practica"y impossible to teach good programming style to

    students that have had prior exposure to BASIC: as potential programmers they are menta"y mutilated beyond hope of regeneration.” – Edsger W. Dijkstra Friday, June 12, 2009
  6. IDENTIFICATION DIVISION. PROGRAM-ID. COBOL-EXAMPLE. PROCEDURE DIVISION. MAIN. IF YEAR <=

    1991 DISPLAY 'COBOL! Argh!'. STOP RUN. Friday, June 12, 2009
  7. “The use of COBOL cripples the mind; its teaching should,

    therefore, be regarded as a criminal offense.” – Edsger W. Dijkstra Friday, June 12, 2009
  8. “[T]he infantile disorder”, by now nearly 20 years old, is

    hopelessly inadequate for whatever computer application you have in mind today: it is now too clumsy, too risky, and too expensive to use.” – Edsger W. Dijkstra Friday, June 12, 2009
  9. There’s More Than One Way To Do It, but many

    of them are Wrong Friday, June 12, 2009
  10. What Sysadmins Do All Day and how it explains the

    code they write Friday, June 12, 2009
  11. Short, quick scripts #!/bin/sh for year in 1996 1997 1998

    1999 2000; do rm -rf backups/${year} done echo "Sorry, all backups from the pretend Internet money era have been purged." | /usr/ucb/Mail -s "Your restore request" [email protected] Friday, June 12, 2009
  12. Writing Better Code Or, “It hurts when you do that?

    Don’t do that.” Friday, June 12, 2009
  13. 99 out of 100 developers agree: writing tests leads to

    writing better code Friday, June 12, 2009
  14. I’m not going to tell you which test framework to

    use* * but I like Test::Unit with Shoulda and Factory Girl Friday, June 12, 2009
  15. Still don’t know which one to use? Find out what

    your peers use, and use that one. Friday, June 12, 2009
  16. Testing is an automated way of proving I’m right. (Or,

    at least, that my code is.) Friday, June 12, 2009
  17. A Few Tools I Like Or, “I got your take-home

    message right here” Friday, June 12, 2009
  18. Why RCov Helps • To reach full coverage, you write

    more tests. • As you write more tests, you fix more problems. • As you fix more problems, you write better code! Friday, June 12, 2009
  19. Things To Look Out For • RCov isn’t perfect. •

    It will sometimes miss code that is covered. • It is easy to cheat. • Just because it’s covered doesn’t mean the code is awesome. Friday, June 12, 2009
  20. Finds common smells in your code. "app/controllers/application_controller.rb" -- 3 warnings:

    ApplicationController#username_for calls user.username multiple times (Duplication) ApplicationController#username_for doesn't depend on instance state (Utility Function) ApplicationController#username_for refers to user more than self (Feature Envy) "app/controllers/groups_controller.rb" -- 5 warnings: GroupsController#show calls params multiple times (Duplication) GroupsController#show calls params[:page] multiple times (Duplication) GroupsController#show calls params[:page].to_i multiple times (Duplication) GroupsController#show has approx 6 statements (Long Method) GroupsController#show/block/block is nested (Nested Iterators) "app/controllers/sessions_controller.rb" -- 2 warnings: SessionsController#create calls flash multiple times (Duplication) SessionsController#create has approx 8 statements (Long Method) "app/controllers/user_feeds_controller.rb" -- 3 warnings: UserFeedsController#create calls logger multiple times (Duplication) UserFeedsController#create has approx 6 statements (Long Method) UserFeedsController#create has the variable name 'e' (Uncommunicative Name) "app/helpers/application_helper.rb" -- 14 warnings: ApplicationHelper::feed_date_for doesn't depend on instance state (Utility Function) ApplicationHelper::feed_date_for refers to date more than self (Feature Envy) ApplicationHelper::feedstitch_group_for doesn't depend on instance state (Utility Function) ApplicationHelper::feedstitch_group_for refers to user more than self (Feature Envy) ApplicationHelper::link_to_group_feed has 4 parameters (Long Parameter List) ApplicationHelper::name_for doesn't depend on instance state (Utility Function) Friday, June 12, 2009
  21. Why Reek Helps • Reek finds common anti-patterns. • Anti-patterns

    are bad habits that are worth breaking. • Fewer bad habits == better code! Friday, June 12, 2009
  22. Things To Look Out For • Reek also isn’t perfect.

    • It false-positives often. • It can only make suggestions. • It doesn’t catch all possible code smells. Friday, June 12, 2009
  23. Let’s Talk About How My Code Smells Or, The Part

    Of The Presentation That Is Potentia"y Very Embarrassing To The Presenter Friday, June 12, 2009
  24. Do As I Say, Not As I Did Actual client

    project, 2007 (I am not making this up): • One controller • One method • ~130 LOC • Actual comment: # OPTIMIZE: this is ugly Friday, June 12, 2009
  25. xxxxx XxxxxxXxxxxxxxxx < XxxxxxxxxxxXxxxxxxxxx # redacted for public publication xxxxxx_xxxxxx

    :xxxxx_xxx_xxxx xxxxxx_xxxxxx :xxxxxxx_xxx_xx_xxxxxxx, :xxxx => [:xxxx]‚Ä® xxx xxxx xxxxxx.xxxxxx(:xx_xxx_xxxxxxx) xxxxxx[:xxxxxx] ||= Xxxxxxx::XXXXXX_XXXXXX @xxxxxxx_xxxxxxx = xxxxxx.xxxxxx(:xxxxxxx_xxxxxxx) xx xxxxxx[:xxxxxxxx_xxxx_xxxxxxx].xxx? @xxxx_xxxxxxxxxx_xxx_xxxxxxx = xxxx xxxx @xxxx_xxxxxxxxxx_xxx_xxxxxxx = xxxxxx[:xxxxxxxx_xxxx_xxxxxxx].xxxxxxx?("xxxx_xxxxxxxxxx") xx @xxxx_xxxxxxxxxx_xxx_xxxxxxx xxxxxx[:xxxxxxxx_xxxx_xxxxxxx].xxxxxx("xxxx_xxxxxxxxxx") xxxxxxxx_xxxxxx = Xxxx[*xxxxxxx[:xxxxxx]['xxxxxxxx_xxxx_xxxxx']] xx xxxxxxxx_xxxxxx.xxxxxx > 9 xxxxxxxx_xxxxxx.xxxx{|x,x| x[1] <=> x[1]}[9..-1].xxxx xx |x| xxxxxx[:xxxxxxxx_xxxx_xxxxxxx] << x[0] xxx xxx xxx xxx‚Ä® xx xxxxxx[:xxxx_xxxxxxx].xxx? @xxxx_xxxxxx_xxx_xxxxxxx = xxxx xxxx @xxxx_xxxxxx_xxx_xxxxxxx = xxxxxx[:xxxx_xxxxxxx].xxxxxxx?("xxxx_xxxxxx") xx @xxxx_xxxxxx_xxx_xxxxxxx xxxxxx[:xxxx_xxxxxxx].xxxxxx("xxxx_xxxxxx") xxxx_xxxxxx = Xxxx[*xxxxxxx[:xxxxxx]['xxxx_xxxxx']] xx xxxx_xxxxxx.xxxxxx > 19 xxxx_xxxxxx.xxxx{|x,x| x[1] <=> x[1]}[19..-1].xxxx xx |x| xxxxxx[:xxxx_xxxxxxx] << x[0] xxx xxx xxx xxx‚Ä® @xxxxxx = xxxxxxx_xxxxxx(xxxxxx[:xxxxxxxx]) xxxxxx @xxxxxx.xxx? xxxxxx[:xxxxxxxx] = xxxxxxxxxx_xxxxxxx(@xxxxxx) xxxxxxx_xxxxxx(:xxxxxx => xxxxxx[:xxxxxx], :xxxxxx_xxx => xxxxxx[:xxxxxx_xxx], :xx_xxxxxxxx => xxxxxx[:xx_xxxxxxxx], :xxxxx => xxxx, :xxxxxx => xxxxxx[:xxxxxx].xx_x) xxxx xxxxx[:xxxxxx] = "Xxxxxxx xxxxxxxx, xxxxxx xxx xxxx xxxxxx xxxxx." @xxxxxxxx = [].xxxxxxxx(:xxxx => 1) xxx @xxxx_xxxxxxx = @xxxxxxxx[0,3] @xxxxxxxxxx_xxxxxxx = (@xxxxxxxx[3,(Xxxxxxx.xxx_xxxx - 3)] || []) @xxxxxxxx = @xxxxxxxx.xxxxxx xx @xxxxxxxx.xxxxxxx_xx?(:xxxxxx) && [email protected]? && [email protected]? @xxxxx_xxxxxx = Xxxx[*@xxxxxxxx.xxxxxx['xxxxx_xxxxx']] @xxxx_xxxxxx = Xxxx[*@xxxxxxxx.xxxxxx['xxxx_xxxxx']] @xxxxxxxx_xxxx_xxxxxx = Xxxx[*@xxxxxxxx.xxxxxx['xxxxxxxx_xxxx_xxxxx']] xxxxxx_xxxxx_xxxxxx = Xxxx[*xxxxxxx[:xxxxxx]['xxxxx_xxxxx']] xxxxxx_xxxx_xxxxxx = Xxxx[*xxxxxxx[:xxxxxx]['xxxx_xxxxx']] xxxxxx_xxxxxxxx_xxxx_xxxxxx = Xxxx[*xxxxxxx[:xxxxxx]['xxxxxxxx_xxxx_xxxxx']]‚Ä® xxxxxx_xxxxx_xxxxxx.xxxx xx |x,x| @xxxxx_xxxxxx[x] = 0 xxxxxx @xxxxx_xxxxxx.xxx_xxx?(x) xxx xxxxxx_xxxx_xxxxxx.xxxx xx |x,x| @xxxx_xxxxxx[x] = 0 xxxxxx @xxxx_xxxxxx.xxx_xxx?(x) xxx xxxxxx_xxxxxxxx_xxxx_xxxxxx.xxxx xx |x,x| @xxxxxxxx_xxxx_xxxxxx[x] = 0 xxxxxx @xxxxxxxx_xxxx_xxxxxx.xxx_xxx?(x) xxx‚Ä® @xxxxx_xxxxxx = @xxxxx_xxxxxx.xxxx # OPTIMIZE: this is ugly @xxxx_xxxxxx = @xxxx_xxxxxx.xxxx{|x,x| "#{xxxxxxx('%09x',xxxxxx_xxxx_xxxxxx[x[0]])} #{x[0]}" <=> "#{xxxxxxx('%09x',xxxxxx_xxxx_xxxxxx[x[0]])} #{x[0]}"} @xxxxxxxx_xxxx_xxxxxx = @xxxxxxxx_xxxx_xxxxxx.xxxx{|x,x| "#{xxxxxxx('%09x',xxxxxx_xxxxxxxx_xxxx_xxxxxx[x[0]])} #{x[0]}" <=> "#{xxxxxxx('%09x',xxxxxx_xxxxxxxx_xxxx_xxxxxx[x[0]])} #{x[0]}"}‚Ä® @xxxxxxxx_xxxxxx = @xxxxxxxx.xxxxxx xxxxxxxx_xxx = [] xxxxxxxx_xxx = [] xxxxxx xxxxxxx[:xxxxxx]['xxxxxxxx_xxxxx_xxxxx'].xxx? xxxxxxxx_xxx = Xxxx[*xxxxxxx[:xxxxxx]['xxxxxxxx_xxxxx_xxxxx']].xxxxxx_xx{|x,x| x.xxxxx?}.xxxxxxx{|x,x| x.xx_x} xxx xxxxxx xxxxxxx[:xxxxxx]['xxxxxxxx_xxxxx_xxxxx'].xxx? xxxxxxxx_xxx = Xxxx[*xxxxxxx[:xxxxxx]['xxxxxxxx_xxxxx_xxxxx']].xxxxxx_xx{|x,x| x.xxxxx?}.xxxxxxx{|x,x| x.xx_x} xxx xxxxxxx_xxxxxxx = [] (xxxxxxxx_xxx+xxxxxxxx_xxx).xxxx xx |xxx| xxxxx xxxxxxx_xxxxxxx << Xxxxxx.xxxx(xxx) xxxxxx XxxxxxXxxxxx::XxxxxxXxxXxxxx @xxxxxxxx.xxxx{|x| x.xxxxx_xxx_xxxxx} xxx xxx @xxxxxxx = @xxxxxxx_xxxx.xxxxxxx_xxxxxxx.xxxxxx{|xx| xxxxxxx_xxxxxxx.xxxxxxx?(xx.xxxxxx) && xx.xxxxxx.xxxxxxxxxx_xxxx=='Xxxx' && @xxxxxxx_xxxx.xxxxxxxx_xxx_xxxxxxx_xxxx.xxxxxxx? (xx.xxxxxx.xxxxxxxxxx.xxxxxxxx_xxx)} @xxx_xxxxxxx = @xxxxxxx_xxxx.xxxxxxx_xxxxxxx.xxxxxx{|xx| xxxxxxx_xxxxxxx.xxxxxxx?(xx.xxxxxx) && (xx.xxxxxx.xxxxxxxxxx_xxxx!='Xxxx' || !@xxxxxxx_xxxx.xxxxxxxx_xxx_xxxxxxx_xxxx.xxxxxxx? (xx.xxxxxx.xxxxxxxxxx.xxxxxxxx_xxx))} xxxx_xxxxxxx_xxx = @xxxxxxx.xxxxxxx{|xx| xx.xxxxxx.xx} & (xxxxxx[:xxxxxx_xxx] || []).xxxxxxx{|xx| xx.xx_x} xxxx_xxx_xxxxxxx_xxx = @xxx_xxxxxxx.xxxxxxx{|xx| xx.xxxxxx.xx} & (xxxxxx[:xxxxxx_xxx] || []).xxxxxxx{|xx| xx.xx_x} @xxxx_xxxxxxx_xxxxx = xxxx_xxxxxxx_xxx.xxxxxx @xxxx_xxx_xxxxxxx_xxxxx = xxxx_xxx_xxxxxxx_xxx.xxxxxx @xxxx_xxxxxx_xxx = xxxx_xxxxxxx_xxx + xxxx_xxx_xxxxxxx_xxx @xxxxxxx.xxxx!{|x,x| (@xxxx_xxxxxx_xxx.xxxxxxx?(x.xxxxxx_xx) ? 1 : 0) <=> (@xxxx_xxxxxx_xxx.xxxxxxx?(x.xxxxxx_xx) ? 1 : 0)} @xxx_xxxxxxx.xxxx!{|x,x| (@xxxx_xxxxxx_xxx.xxxxxxx?(x.xxxxxx_xx) ? 1 : 0) <=> (@xxxx_xxxxxx_xxx.xxxxxxx?(x.xxxxxx_xx) ? 1 : 0)} @xxx_xxxxxxx = xxxxxxx_xxxxxxx - ([@xxxxxxx_xxxx.xxxxxx] + @xxxxxxx.xxxxxxx{|x| x.xxxxxx} + @xxx_xxxxxxx.xxxxxxx{|xx| xx.xxxxxx}) @xxx_xxxxxxx = @xxx_xxxxxxx.xxxxxx{|x| x.xxxxxxxxxx_xxxx == 'Xxxxxxx'} @xxx_xxxxxxx.xxxx!{|x,x| (x.xxxxxxxxxx_xxxx=='Xxxxx' ? 1 : 0) <=> (x.xxxxxxxxxx_xxxx=='Xxxxx' ? 1 : 0)} @xxx_xxxxxxx = @xxx_xxxxxxx[0,3] xxxx @xxxxx_xxxxxx = [] @xxxx_xxxxxx = [] @xxxxxxxx_xxxx_xxxxxx = [] @xxxxxxxx_xxxxxx = [] @xxxxxxx = [] @xxx_xxxxxxx = [] @xxxx_xxxxxx_xxx = [] @xxx_xxxxxxx = [] @xxxx_xxxxxxx_xxxxx = 0 @xxxx_xxx_xxxxxxx_xxxxx = 0 xxx @xxxxxxx_xxxxxxxx_xxxxxxx = [] @xxxxxxx_xxxx_xxxxxxx = [] @xxxxxxx_xxxxx_xxxxxxx = [] xx xxxxxx[:xxxxxxxx_xxxx_xxxxxxx] && xxxxxx[:xxxxxxxx_xxxx_xxxxxxx].xxxx.xxxxxx != @xxxxxxxx_xxxx_xxxxxx.xxxxxx @xxxxxxx_xxxxxxxx_xxxxxxx = xxxxxx[:xxxxxxxx_xxxx_xxxxxxx] xxx xx xxxxxx[:xxxx_xxxxxxx] && xxxxxx[:xxxx_xxxxxxx].xxxx.xxxxxx != @xxxx_xxxxxx.xxxxxx @xxxxxxx_xxxx_xxxxxxx = xxxxxx[:xxxx_xxxxxxx] xxx xx xxxxxx[:xxxxx_xxxxxxx] && xxxxxx[:xxxxx_xxxxxxx].xxxx.xxxxxx != @xxxxx_xxxxxx.xxxxxx @xxxxxxx_xxxxx_xxxxxxx = xxxxxx[:xxxxx_xxxxxxx] xxx xx @xxxxxxx_xxxxxxxx_xxxxxxx.xxxxx? && @xxxxxxx_xxxx_xxxxxxx.xxxxx? && @xxxxxxx_xxxxx_xxxxxxx.xxxxx? xxxxxx [xxxxxx[:xxxxxxxx_xxxx_xxxxxxx],xxxxxx[:xxxx_xxxxxxx],xxxxxx[:xxxxx_xxxxxxx]] == [xxx,xxx,xxx] @xxxxxxx_xxxxxxx = xxxx xxx xxx xxx xxx‚Ä® Friday, June 12, 2009
  26. Why??? •Didn’t understand the domain. •Not thinking in object-oriented paradigm.

    •Not thinking in MVC. •Not pairing. •Lousy tests. Friday, June 12, 2009
  27. What Really Goes In A Controller? •Code you need to

    respond to a web request and render the result •Not business logic •Not complex operations on objects Friday, June 12, 2009
  28. Controller Liposuction •Everything in its right place. •When you see

    yourself doing business logic, move it to the model! •When you see yourself defining view logic, move it to the view/ helper! Friday, June 12, 2009
  29. Finding Big Methods •Too much stuff happening; too many concerns

    •Too many LOC to comprehend in a glance •Logic inside logic Friday, June 12, 2009
  30. def response if @response.nil? @final_response = false current = source

    path = source.query.nil? ? source.path : "#{source.path}?#{source.query}" until @final_response Net::HTTP.start(current.host, current.port) do |http| @response = http.get(path, 'User-Agent' => user_agent) if @response.header['location'] current = URI.parse(@response.header['location']) path = current.query.nil? ? current.path : "#{current.path}?#{current.query}" else @final_response = true end end end end @response end Friday, June 12, 2009
  31. Remember UNIX? Small tools doing one job well ⇒ small

    methods doing one job well Friday, June 12, 2009
  32. Name Methods To Communicate Intent thing.expires_at < Time.now 㱺 thing.expired?

    user.has_accepted_terms && !user.suspended 㱺 user.can_sign_in? Friday, June 12, 2009
  33. Don’t Do Too Much In A Method •When you see

    a method getting too long, break it into sma"er methods. • Hint: look for excessive indentation and nested ifs. • Hint: a method shouldn’t come close to filling your editor window. Friday, June 12, 2009
  34. def self.user_agent "Awesome App Ruby/#{RUBY_VERSION}" end def self.maximum_redirects 4 end

    def initialize(url) @url = url end def endpoint_for(uri) endpoint = uri.path.sub(/^\/?$/, '/') endpoint += "?#{uri.query}" unless uri.query.blank? endpoint end def fetch(url, redirects_remaining) return if redirects_remaining == 0 begin uri = URI.parse(url) response = Net::HTTP.start(uri.host, uri.port) do |http| http.get(endpoint_for(uri), {'User-Agent' => self.class.user_agent}) end if response.is_a?(Net::HTTPRedirection) response = fetch(response['location'], redirects_remaining - 1) end rescue URI::InvalidURIError RAILS_DEFAULT_LOGGER.warn "Error parsing URL: '#{url}'" end response end def response @response ||= fetch(@url, self.class.maximum_redirects) end Friday, June 12, 2009
  35. Self-Review • Don’t check it in if the tests fail.

    • Use continuous integration to keep yourself honest. (more on this later) • Look over all your changes before you commit. • If you miss something and you’re using Git, fix it and git commit --amend Friday, June 12, 2009
  36. Peer Review • Pair-program whenever you can. Pair programming comes

    with built-in peer review. • Do regular code reviews with your peers. During active development, weekly is good. • Listen to criticism. Don’t take it persona"y. No one’s perfect. Friday, June 12, 2009
  37. Quality Takes Time By law, straight bourbon must be aged

    in new, charred oak barrels for at least two years. Anything less yields just whiskey, not bourbon. Friday, June 12, 2009
  38. You may not have two to four years, but you

    sti" shouldn’t rush Friday, June 12, 2009
  39. Working In A Vacuum Most of us don’t. No one

    should. Friday, June 12, 2009
  40. Let’s Wrap This Up Or, “I Hope You Know This

    Wi" Go Down On Your Permanent Record” Friday, June 12, 2009
  41. Use tools to help you, not to do work for

    you. Friday, June 12, 2009
  42. The End. This has been “Code Stinkers Anonymous” by Mark

    Cornick. Thank you! http://objectsinmirrors.com/ http://twitter.com/mcornick Please rate this talk on SpeakerRate! http://speakerrate.com/talks/1170 Friday, June 12, 2009