ELI5

Building A rails app for 5 year olds

(Not really)

Justin Herrick

 

 

 

Explain

(it)

(to me)

like I'm five

https://www.reddit.com/r/explainlikeimfive/

Reddit 🙄

How do?

  • Simplify

  • Analogize

  • Use Progressive Disclosure

  • Provided Resources

  • Educate

Lets do that!

EVERYONE KNOWS THAT DEBUGGING IS TWICE AS HARD AS WRITING A PROGRAM IN THE FIRST PLACE. SO IF YOU'RE AS CLEVER AS YOU CAN BE WHEN YOU WRITE IT, HOW WILL YOU EVER DEBUG IT?

- Brian Kernighan

the three great virtues of a programmer: laziness, impatience, and hubris

- Larry Wall

So What are we aiming for?

robustness
 

onboarding
 

Debugging

Whats going wrong?

Complexity
Bad Abstractions
Bad Code

bad tests

(Or No Abstractions)

(Or No Tests)

(In a rails app? Never!)

We Can do better

Patterns

"Cognitive Offboarding"

A shared language

A common vocabulary

A toolbox full of hammers

Can be used and abused

Design Patterns

So many patterns, so little time

  • Factory Method
  • Null Object
  • Active Record
  • Singleton
  • Adapter
  • Bridge
  • Decorator
  • Memento
  • Controller
  • Visitor
  • Command
  • Guard Clause

Dont just use patterns because they exist

Document when and why you're using a particular pattern

establish system patterns

Patterns are consistent

Patterns are predictable

(The principle of least astonishment)

system patterns establish truth

system patterns establish reasoning

system patterns provoke conversation

How to design a pattern

build for emulation

Restrict the design space

Add Fail-safes and defaults

document

write some tests.

(Please)

Lets go deeper

Prefer small objects over generic data structures

(Avoiding Primitive Obsession)

generic data structures

def fetch_invoices(account_id)
  response = Invoice::Fetcher.new(account_id).get_all
  
  if response.status_code == OKAY
    {
      status: OKAY,
      invoices: response.data
    }
  else
    {
      status: ERROR,
      invoices: nil,
      error: "Couldn't fetch"
    }
  end  
end

Small objects

def fetch_invoices(account_id)
  response = Invoice::Fetcher.new(account_id).get_all
  
  if response.status_code == OKAY
    InvoiceOkayResponse.new(response.data)
  else
    InvoiceErrorResponse.new(response.error)
  end  
end

Small objects

def fetch_invoices(account_id)
  response = Invoice::Fetcher.new(account_id).get_all
  
  InvoiceResponder.process(response)
end

Localize state

(Avoid shared mutable state)

non-Localize state

    def prepare_events
      if events_exist?
        @presenter_one = @next_event.main.presenter_one.last_name
        @presenter_two = @next_event.main.presenter_two.last_name
      elsif @next_event.try(:title_tag_line)
        presenters     = @next_event.tag_line.split(tag_line_del)
        @presenter_one = presenters.first
        @presenter_two = presenters.last
      else
        @presenter_one = @prev_event.main.presenter_one.last_name
        @presenter_two = @prev_event.main.presenter_two.last_name
      end
    end

Localized state

    def prepare_events(next_event, prev_event = nil)
      if events_exist?
        [ next_event.main.presenter_one.last_name,
          next_event.main.presenter_two.last_name ]
      elsif next_event.try(:title_tag_line)
        presenters     = next_event.tag_line.split(tag_line_del)
        [ presenters.first,
          presenters.last ]
      else
        [ prev_event.main.presenter_one.last_name
          prev_event.main.presenter_two.last_name ]
      end
    end

    def show
      # ... some code
      @presenter_one, 
        @presenter_two = prepare_events(@next_event, @prev_event)
    end

Localized state

    def event_presenters(next_event, prev_event = nil)
      if events_exist?(next_event)

        [ next_event.first_presenter, next_event.second_presenter ]

      elsif next_event.try(:title_tag_line)
        presenters = next_event.tag_line.split(tag_line_del)

        [ presenters.first, presenters.last ]

      elsif prev_event

        [ prev_event.first_presenter, prev_event.second_presenter ]
      end
    end

make order-dependence explicit

Unexplicit order dependence


    before_filter :run_before
    before_filter :prepare_event_page, only: [:show_without_cache, :show]
    before_filter :set_events, only: [:show_without_cache, :show]
    around_filter :set_visitor, only: [:show_without_cache, :show]
    after_filter  :run_analytics_job, if: Proc.new{|c| !@next_event.present? }
    before_filter :parse_query

    
    def show
      if !@query.nil?
        #...
      else
        #...
      end
    end

    def show_without_cache
      if @event.nil?
        render text: "Not Found", status: 404
      end
    end
 

explicit order dependence

    def show
      public_event_display do |events, visitor, query|
        if query
          #...
        else
          #...
        end
      end
    end

    def show_without_cache
      public_event_display do |events, _, _|
        if events.empty?
          render text: "Not Found", status: 404
        end
      end
    end
 
    private

    def public_event_display
      run_before
      prepare_evnet_page

      events  = get_events
      visitor = get_visitor
      query   = parse_query(params)

      yield(events, visitor, query)
      AnalyticsJob.perform(visitor, query) if has_next_event?(events)
    end

explicit order dependence

    def show
      public_event_display do |events, visitor, query|
        if query
          #...
        else
          #...
        end
      end
    end

    def show_without_cache
      public_event_display do |events, _, _|
        if events.empty?
          render text: "Not Found", status: 404
        end
      end
    end
 
    private

    def public_event_display
      run_before
      prepare_evnet_page

      events  = get_events
      visitor = get_visitor
      query   = parse_query(params)

      yield(events, visitor, query)
      AnalyticsJob.perform(visitor, query) if has_next_event?(events)
    end

Remove base assumptions

Review base assumptions

Remove Bad assumptions

Validate

Repeat

Questions?

Thanks!

Made with Slides.com