Skip to content

Instantly share code, notes, and snippets.

@jschoolcraft
Forked from jraines/confident-code.md
Created May 18, 2011 23:28
Show Gist options
  • Save jschoolcraft/979827 to your computer and use it in GitHub Desktop.
Save jschoolcraft/979827 to your computer and use it in GitHub Desktop.
Notes on Avdi Grimm's "Confident Code"

Confident Code

timid code

  • Randomly mixes input gathering, error handling, and business logic
  • imposes cognitive load on the reader

confident code

  • no digressions
  • consistent narrative structure

Example code with this talk

http://github.com/avdi/cowsay

Good order

  1. Gather input
  2. Perform work
  3. Deliver results
  4. Handle Failure

Gather input

Dealing with uncertain input

  • Coerce
    • Use #to_s, #to_i, and #to_a liberally
    • Array(something) is better than #to_a in Ruby 1.9
    • Use a decorator (presenter)
  • Reject
  • Ignore
    • guard clause, ie return "" if message.nil?
    • Special case you can't ignore? Special Case pattern
      • represent special case as an object
    • Use a "black hole Null Object"
class NullObject
  def initialize
    @origin = caller.first
  end

  def __null_origin__
    @origin
  end

  def method_missing(*args, &block)
    self
  end

  def nil?
    true
  end
end

def Maybe(value)
  value.nil? ? NullObject.new : value
end

Zero tolerance for nil

  • nil is overused in Ruby
  • it can mean a lot of things
  • use Hash#fetch when appropriate
    • collection.fetch(key) { fallback_actions }
  • Use a NullObject as default
    • NullObject can log its origin

Perform Work

Conditionals for Business Logic

  • Reserve conditionals for business logic
  • Don't use for nil catching, error handling

Confident Styles of Work

  • Chaining -- better with a smart Null Object and Maybe method
  • Iteration
    • Single object operations are implicitly one-or-error
    • Iteration is implicitly 0-or-more, not 1-or-fail
    • Chains of enumerable operations are self-nullifying

Return Results

  • Return a Special Case or Null Object

Handle Failure

  • Put the happy path first
  • Put error handling at the end
  • Or in other methods
  • Put a rescue in a method rather than wrapping method calls in begin . . . rescue . . . end block
  • Extract error handling methods

Extract error handling methods

  • Checked Method
  def checked_popen(command, mode, fail_action)
      check_child_exit_status do
        @io_class.popen(command, "w+") do |process|
          yield(process)
        end
      end
    rescue Errno::EPIPE
      fail_action.call
    end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment