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

Bringing UX to Your Code, V2

Bringing UX to Your Code, V2

User Centered Design as a process is almost thirty years old now. The philosophy has permeated our products and the way we build our interfaces. But this philosophy is rarely extended to the code we write. We'll take a look at some principles of UX and Interface Design and relate them back to our code. By comparing code that gets it right to code that gets it desperately wrong, we'll learn some principles that we can use to write more usable code.

Joseph Mastey

May 05, 2017
Tweet

More Decks by Joseph Mastey

Other Decks in Programming

Transcript

  1. Bringing UX to Your Code
    Joe Mastey
    @jmmastey

    View full-size slide

  2. @jmmastey
    Joe Mastey
    @jmmastey
    josephmastey.com
    chicagoapprenticeships.com

    View full-size slide

  3. @jmmastey
    I help companies build awesome
    internal education programs

    View full-size slide

  4. @jmmastey
    I’ve worked in software for
    a million years.

    View full-size slide

  5. @jmmastey
    I am not a UX designer / developer

    View full-size slide

  6. @jmmastey
    “Norman discussed door handles
    at length.” - Wikipedia

    View full-size slide

  7. It’s not your fault

    View full-size slide

  8. @jmmastey
    Things We Ignore:

    View full-size slide

  9. @jmmastey
    Things We Ignore:
    1. Anything invented a long time ago

    View full-size slide

  10. @jmmastey
    Things We Ignore:
    1. Anything invented a long time ago
    2. Anything invented by non-engineers

    View full-size slide

  11. @jmmastey
    <
    decades of research shit I made up

    View full-size slide

  12. @jmmastey
    We’re missing out

    View full-size slide

  13. @jmmastey
    Our code is an interface

    View full-size slide

  14. @jmmastey
    Our users are stressed

    View full-size slide

  15. @jmmastey
    Let’s try it!

    View full-size slide

  16. @jmmastey
    Gulf of Execution

    View full-size slide

  17. @jmmastey
    Is there an action that corresponds
    to my intentions?

    View full-size slide

  18. @jmmastey
    An example: deleting an
    ActiveRecord object

    View full-size slide

  19. @jmmastey
    macguffin.methods.sort

    View full-size slide

  20. @jmmastey
    [:!, :!=, :!~, :<=>, :==, :===, :=~, :Pathname, :[], :
    []=, :__class__, :__extend__, :__id__, :__instance_variable_defined_p__, :__instance_variable_get__, :__instance_variable_set__, :__instance_variables__, :__marshal__, :__respond_to_p__, :__send__
    , :__show__, :_commit_callbacks, :_commit_callbacks=, :_commit_callbacks?, :_create_callbacks, :_create_callbacks=, :_create_callbacks?, :_destroy, :_destroy_callbacks, :_destroy_callbacks=, :_des
    troy_callbacks?, :_find_callbacks, :_find_callbacks=, :_find_callbacks?, :_initialize_callbacks, :_initialize_callbacks=, :_initialize_callbacks?, :_read_attribute, :_reflections, :_reflections=,
    :_reflections?, :_rollback_callbacks, :_rollback_callbacks=, :_rollback_callbacks?, :_run_commit_callbacks, :_run_create_callbacks, :_run_destroy_callbacks, :_run_find_callbacks, :_run_initialize_
    callbacks, :_run_rollback_callbacks, :_run_save_callbacks, :_run_touch_callbacks, :_run_update_callbacks, :_run_validate_callbacks, :_run_validation_callbacks, :_save_callbacks, :_save_callbacks=,
    :_save_callbacks?, :_touch_callbacks, :_touch_callbacks=, :_touch_callbacks?, :_update_callbacks, :_update_callbacks=, :_update_callbacks?, :_validate_callbacks, :_validate_callbacks=, :_validate_
    callbacks?, :_validation_callbacks, :_validation_callbacks=, :_validation_callbacks?, :_validators, :_validators=, :_validators?, :`, :acts_like?, :actually_destroyed?, :add_to_transaction, :after
    _add_for_categories, :after_add_for_categories=, :after_add_for_categories?, :after_add_for_enrollments, :after_add_for_enrollments=, :after_add_for_enrollments?, :after_add_for_skills, :after_add
    _for_skills=, :after_add_for_skills?, :after_add_for_users, :after_add_for_users=, :after_add_for_users?, :after_remove_for_categories, :after_remove_for_categories=, :after_remove_for_categories?
    , :after_remove_for_enrollments, :after_remove_for_enrollments=, :after_remove_for_enrollments?, :after_remove_for_skills, :after_remove_for_skills=, :after_remove_for_skills?, :after_remove_for_u
    sers, :after_remove_for_users=, :after_remove_for_users?, :aggregate_reflections, :aggregate_reflections=, :aggregate_reflections?, :approve, :approve!, :approve_transition, :approved?, :arel_attr
    ibutes_with_values_for_create, :arel_attributes_with_values_for_update, :as_json, :assign_attributes, :association, :association_cache, :attribute_aliases, :attribute_aliases?, :attribute_changed?
    , :attribute_changed_in_place?, :attribute_for_inspect, :attribute_method?, :attribute_method_matchers, :attribute_method_matchers?, :attribute_missing, :attribute_names, :attribute_present?, :att
    ribute_was, :attributes, :attributes=, :attributes_before_type_cast, :attributes_changed_by_setter, :autosave_associated_records_for_categories, :autosave_associated_records_for_enrollments, :auto
    save_associated_records_for_skills, :autosave_associated_records_for_users, :becomes, :becomes!, :before_add_for_categories, :before_add_for_categories=, :before_add_for_categories?, :before_add_f
    or_enrollments, :before_add_for_enrollments=, :before_add_for_enrollments?, :before_add_for_skills, :before_add_for_skills=, :before_add_for_skills?, :before_add_for_users, :before_add_for_users=,
    :before_add_for_users?, :before_remove_for_categories, :before_remove_for_categories=, :before_remove_for_categories?, :before_remove_for_enrollments, :before_remove_for_enrollments=, :before_remo
    ve_for_enrollments?, :before_remove_for_skills, :before_remove_for_skills=, :before_remove_for_skills?, :before_remove_for_users, :before_remove_for_users=, :before_remove_for_users?, :blank?, :ca
    che_key, :cache_timestamp_format, :cache_timestamp_format?, :can_approve?, :can_deprecate?, :can_hide?, :can_publish?, :capture, :categories, :categories=, :category_ids, :category_ids=, :changed,
    :changed?, :changed_attributes, :changed_for_autosave?, :changes, :changes_applied, :class, :class_eval, :clear_aggregation_cache, :clear_association_cache, :clear_changes_information, :clear_dest
    roy_state, :clear_transaction_record_state, :clone, :clone_attribute_value, :column_for_attribute, :committed!, :concern, :connection_handler, :created?, :created_at, :created_at=, :created_at?, :
    created_at_before_type_cast, :created_at_change, :created_at_changed?, :created_at_was, :created_at_will_change!, :decrement, :decrement!, :deep_dup, :default_connection_handler, :default_connecti
    on_handler?, :default_scopes, :default_timezone, :define_singleton_method, :defined_enums, :defined_enums=, :defined_enums?, :delete, :deprecate, :deprecate!, :deprecate_transition, :deprecated?,
    :description, :description=, :description?, :description_before_type_cast, :description_change, :description_changed?, :description_was, :description_will_change!, :destroy, :destroy!, :destroyed?
    , :destroyed_by_association, :destroyed_by_association=, :display, :dump_schema_after_migration, :dup, :duplicable?, :enable_warnings, :encode_with, :enrollment_ids, :enrollment_ids=, :enrollments
    , :enrollments=, :enum_for, :eql?, :equal?, :error_on, :errors, :errors_on, :extend, :find_by_statement_cache, :find_by_statement_cache=, :find_by_statement_cache?, :fire_events, :fire_events!, :f
    ire_status_event, :force_clear_transaction_record_state, :freeze, :from_json, :from_xml, :frozen?, :gem, :handle, :handle=, :handle?, :handle_before_type_cast, :handle_change, :handle_changed?, :h
    andle_was, :handle_will_change!, :has_attribute?, :has_transactional_callbacks?, :hash, :hidden?, :hide, :hide!, :hide_transition, :html_safe?, :human_status_name, :id, :id=, :id?, :id_before_type
    _cast, :id_change, :id_changed?, :id_was, :id_will_change!, :in?, :include_root_in_json, :include_root_in_json=, :include_root_in_json?, :increment, :increment!, :init_with, :initialize_internals_
    callback, :initialize_state_machines, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_values, :instance_variable_defined?, :instance_variable_get, :instance_variable_names, :ins
    tance_variable_set, :instance_variables, :invalid?, :is_a?, :is_haml?, :itself, :kind_of?, :load_dependency, :lock!, :lock_optimistically, :lock_optimistically?, :locking_enabled?, :logger, :mark_
    for_destruction, :marked_for_destruction?, :method, :method_missing, :methods, :model_name, :name, :name=, :name?, :name_before_type_cast, :name_change, :name_changed?, :name_was, :name_will_chang
    e!, :nested_attributes_options, :nested_attributes_options?, :new_record?, :nil?, :no_touching?, :object_id, :organization, :organization=, :organization?, :organization_before_type_cast, :organiz
    ation_change, :organization_changed?, :organization_was, :organization_will_change!, :partial_writes, :partial_writes?, :perform_validations, :persisted?, :pluralize_table_names, :pluralize_table_
    names?, :populate_with_current_scope_attributes, :presence, :presence_in, :present?, :pretty_inspect, :pretty_print, :pretty_print_cycle, :pretty_print_inspect, :pretty_print_instance_variables, :
    previous_changes, :primary_key_prefix_type, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :publish, :publish!, :publish_transition, :published?, :query_attri
    bute, :quietly, :quietly_with_deprecation_silenced, :quietly_without_deprecation_silenced, :quoted_id, :raise_in_transactional_callbacks, :raise_record_invalid, :read_attribute, :read_attribute_be
    fore_type_cast, :read_attribute_for_serialization, :read_attribute_for_validation, :read_store_attribute, :readonly!, :readonly?, :record_timestamps, :record_timestamps=, :record_timestamps?, :rel
    oad, :remember_transaction_record_state, :remove_instance_variable, :require_dependency, :require_or_load, :reset_created_at!, :reset_description!, :reset_handle!, :reset_id!, :reset_name!, :reset
    _organization!, :reset_status!, :reset_updated_at!, :respond_to?, :respond_to_without_attributes?, :restore_attributes, :restore_created_at!, :restore_description!, :restore_handle!, :restore_id!,
    :restore_name!, :restore_organization!, :restore_status!, :restore_transaction_record_state, :restore_updated_at!, :rollback_active_record_state!, :rolledback!, :run_callbacks, :run_validations!,
    :sanitize_for_mass_assignment, :sanitize_forbidden_attributes, :save, :save!, :schema_format, :send, :serializable_hash, :set_transaction_state, :should, :should_not, :silence, :silence_stderr, :s
    ilence_stream, :silence_warnings, :singleton_class, :singleton_class?, :singleton_methods, :skill_ids, :skill_ids=, :skills, :skills=, :skip_time_zone_conversion_for_attributes, :skip_time_zone_co
    nversion_for_attributes?, :slice, :status, :status=, :status?, :status_before_type_cast, :status_change, :status_changed?, :status_event, :status_event=, :status_event_transition, :status_event_tr
    ansition=, :status_events, :status_name, :status_paths, :status_transitions, :status_was, :status_will_change!, :store_full_sti_class, :store_full_sti_class?, :suppress, :suppress_warnings, :syck_
    to_yaml, :table_name_prefix, :table_name_prefix?, :table_name_suffix, :table_name_suffix?, :taguri, :taguri=, :taint, :tainted?, :tap, :time_zone_aware_attributes, :timestamped_migrations, :to_enu
    m, :to_gid, :to_global_id, :to_json, :to_json_with_active_support_encoder, :to_json_without_active_support_encoder, :to_key, :to_model, :to_param, :to_partial_path, :to_query, :to_s, :to_sgid, :to
    _signed_global_id, :to_xml, :to_yaml, :to_yaml_properties, :to_yaml_style, :toggle, :toggle!, :touch, :transaction, :transaction_include_any_action?, :transaction_record_state, :trust, :try, :try!
    , :type_for_attribute, :unloadable, :untaint, :untrust, :untrusted?, :update, :update!, :update_attribute, :update_attributes, :update_attributes!, :update_column, :update_columns, :updated_at, :u
    pdated_at=, :updated_at?, :updated_at_before_type_cast, :updated_at_change, :updated_at_changed?, :updated_at_was, :updated_at_will_change!, :user_ids, :user_ids=, :users, :users=, :valid?, :valid
    ate, :validate!, :validate_associated_records_for_categories, :validate_associated_records_for_enrollments, :validate_associated_records_for_skills, :validate_associated_records_for_users, :valida
    tes_absence_of, :validates_acceptance_of, :validates_confirmation_of, :validates_exclusion_of, :validates_format_of, :validates_inclusion_of, :validates_length_of, :validates_numericality_of, :val
    idates_presence_of, :validates_size_of, :validates_with, :validation_context, :validation_context=, :with_lock, :with_options, :with_transaction_returning_status, :with_warnings, :write_store_attr
    ibute]

    View full-size slide

  21. @jmmastey
    macguffin.methods.grep /…?/

    View full-size slide

  22. @jmmastey
    The model in my head
    is what I use to search.

    View full-size slide

  23. HTTP Verb: DELETE
    SQL Statement: DELETE FROM *
    Arrays: delete, delete_at, delete_if
    Every OS Ever: DELETE

    View full-size slide

  24. @jmmastey
    macguffin.methods.grep /delete/
    => [:delete]

    View full-size slide

  25. @jmmastey
    macguffin.destroy

    View full-size slide

  26. @jmmastey
    It’s completely
    unnecessary friction

    View full-size slide

  27. @jmmastey
    Gulf of Evaluation

    View full-size slide

  28. @jmmastey
    Did it work?

    View full-size slide

  29. @jmmastey
    This is easy(ish) in code

    View full-size slide

  30. @jmmastey
    macguffin.delete
    => #

    View full-size slide

  31. @jmmastey
    macguffin.destroy
    => #

    View full-size slide

  32. @jmmastey
    class MacGuffin
    private
    def self.retrieve

    end
    end

    View full-size slide

  33. @jmmastey
    Natural Mappings

    View full-size slide

  34. @jmmastey
    What does that look like in code?

    View full-size slide

  35. @jmmastey
    my_stuff.sort

    View full-size slide

  36. @jmmastey
    my_stuff.first

    View full-size slide

  37. @jmmastey
    my_stuff.delete :foo

    View full-size slide

  38. @jmmastey
    brew update
    brew upgrade

    View full-size slide

  39. @jmmastey
    path
    realpath
    absolute_path
    dir_path
    realdirpath

    View full-size slide

  40. @jmmastey
    Design for Errors

    View full-size slide

  41. @jmmastey
    Software is screwed up most
    of the time

    View full-size slide

  42. @jmmastey
    undefined is not a function

    View full-size slide

  43. @jmmastey
    “also, screw you”

    View full-size slide

  44. @jmmastey
    Spooky Nulls at a Distance

    View full-size slide

  45. @jmmastey
    Exploit the Power of Constraint

    View full-size slide

  46. @jmmastey
    Make it hard to do it the wrong way

    View full-size slide

  47. @jmmastey
    mysql --i-am-a-dummy

    View full-size slide

  48. @jmmastey
    irb(main):042:0>

    View full-size slide

  49. @jmmastey
    irb(main):042:0> User.last.destroy
    =>

    View full-size slide

  50. Whitney Hess

    View full-size slide

  51. @jmmastey
    “So You Wanna Be a User
    Experience Designer”

    View full-size slide

  52. @jmmastey
    4. Group Related Objects

    View full-size slide

  53. @jmmastey
    Proximity implies relationship

    View full-size slide

  54. @jmmastey
    what about methods
    in a class?

    View full-size slide

  55. @jmmastey
    what about similarly
    named classes / methods?

    View full-size slide

  56. @jmmastey
    macguffin.accounts_add
    macguffin.accounts_delete
    macguffin.accounts_list

    View full-size slide

  57. @jmmastey
    18. Be Consistent

    View full-size slide

  58. @jmmastey
    I’m not saying your code isn’t
    a beautiful and unique snowflake

    View full-size slide

  59. @jmmastey
    rails dbconsole production
    rails console production
    rails server -e production

    View full-size slide

  60. @jmmastey
    def update
    @macguffin = MacGuffin.find(params[:id])

    end
    def show_macguffin
    m = MacGuffin.find(params[:macguffin_id])

    end

    View full-size slide

  61. @jmmastey
    “gulp”.upcase => “GULP”

    View full-size slide

  62. @jmmastey
    “gulp”.upcase => “GULP”
    “GULP”.upcase => “GULP”

    View full-size slide

  63. @jmmastey
    “gulp”.upcase => “GULP”
    “GULP”.upcase => “GULP”
    “gulp”.upcase! => “GULP”

    View full-size slide

  64. @jmmastey
    “gulp”.upcase => “GULP”
    “GULP”.upcase => “GULP”
    “gulp”.upcase! => “GULP”
    “GULP”.upcase! =>

    View full-size slide

  65. @jmmastey
    “gulp”.upcase => “GULP”
    “GULP”.upcase => “GULP”
    “gulp”.upcase! => “GULP”
    “GULP”.upcase! => nil?!

    View full-size slide

  66. @jmmastey
    params[:product_code].upcase!

    View full-size slide

  67. @jmmastey
    params = { product_code: “foO” }
    # do work
    expect(macguffin.upc).to eq(“FOO”)

    View full-size slide

  68. @jmmastey
    Consistent with what?

    View full-size slide

  69. @jmmastey
    16. Use Emotion

    View full-size slide

  70. @jmmastey
    We have an emotional relationship
    with the software we use

    View full-size slide

  71. @jmmastey
    A demonstration

    View full-size slide

  72. @jmmastey
    Rails

    View full-size slide

  73. @jmmastey
    Elm

    View full-size slide

  74. @jmmastey
    SOAP

    View full-size slide

  75. @jmmastey
    See?

    View full-size slide

  76. @jmmastey
    This isn’t about kitsch

    View full-size slide

  77. @jmmastey
    Warmth and kindness make
    software a pleasure to use

    View full-size slide

  78. @jmmastey
    9. Avoid Jargon

    View full-size slide

  79. @jmmastey
    stop_csrfs
    versus
    protect_from_forgery

    View full-size slide

  80. @jmmastey
    AJAX

    View full-size slide

  81. @jmmastey
    def __private_method

    View full-size slide

  82. @jmmastey
    7. Provide Signposts and Cues

    View full-size slide

  83. @jmmastey
    Where you came from,
    where you’re going

    View full-size slide

  84. @jmmastey
    [:!, :!=, :!~, :<=>, :==, :===, :=~, :Pathname, :[], :
    []=, :__class__, :__extend__, :__id__, :__instance_variable_defined_p__, :__instance_variable_get__, :__instance_variable_set__, :__instance_variables__, :__marshal__, :__respond_to_p__, :__send__
    , :__show__, :_commit_callbacks, :_commit_callbacks=, :_commit_callbacks?, :_create_callbacks, :_create_callbacks=, :_create_callbacks?, :_destroy, :_destroy_callbacks, :_destroy_callbacks=, :_des
    troy_callbacks?, :_find_callbacks, :_find_callbacks=, :_find_callbacks?, :_initialize_callbacks, :_initialize_callbacks=, :_initialize_callbacks?, :_read_attribute, :_reflections, :_reflections=,
    :_reflections?, :_rollback_callbacks, :_rollback_callbacks=, :_rollback_callbacks?, :_run_commit_callbacks, :_run_create_callbacks, :_run_destroy_callbacks, :_run_find_callbacks, :_run_initialize_
    callbacks, :_run_rollback_callbacks, :_run_save_callbacks, :_run_touch_callbacks, :_run_update_callbacks, :_run_validate_callbacks, :_run_validation_callbacks, :_save_callbacks, :_save_callbacks=,
    :_save_callbacks?, :_touch_callbacks, :_touch_callbacks=, :_touch_callbacks?, :_update_callbacks, :_update_callbacks=, :_update_callbacks?, :_validate_callbacks, :_validate_callbacks=, :_validate_
    callbacks?, :_validation_callbacks, :_validation_callbacks=, :_validation_callbacks?, :_validators, :_validators=, :_validators?, :`, :acts_like?, :actually_destroyed?, :add_to_transaction, :after
    _add_for_categories, :after_add_for_categories=, :after_add_for_categories?, :after_add_for_enrollments, :after_add_for_enrollments=, :after_add_for_enrollments?, :after_add_for_skills, :after_add
    _for_skills=, :after_add_for_skills?, :after_add_for_users, :after_add_for_users=, :after_add_for_users?, :after_remove_for_categories, :after_remove_for_categories=, :after_remove_for_categories?
    , :after_remove_for_enrollments, :after_remove_for_enrollments=, :after_remove_for_enrollments?, :after_remove_for_skills, :after_remove_for_skills=, :after_remove_for_skills?, :after_remove_for_u
    sers, :after_remove_for_users=, :after_remove_for_users?, :aggregate_reflections, :aggregate_reflections=, :aggregate_reflections?, :approve, :approve!, :approve_transition, :approved?, :arel_attr
    ibutes_with_values_for_create, :arel_attributes_with_values_for_update, :as_json, :assign_attributes, :association, :association_cache, :attribute_aliases, :attribute_aliases?, :attribute_changed?
    , :attribute_changed_in_place?, :attribute_for_inspect, :attribute_method?, :attribute_method_matchers, :attribute_method_matchers?, :attribute_missing, :attribute_names, :attribute_present?, :att
    ribute_was, :attributes, :attributes=, :attributes_before_type_cast, :attributes_changed_by_setter, :autosave_associated_records_for_categories, :autosave_associated_records_for_enrollments, :auto
    save_associated_records_for_skills, :autosave_associated_records_for_users, :becomes, :becomes!, :before_add_for_categories, :before_add_for_categories=, :before_add_for_categories?, :before_add_f
    or_enrollments, :before_add_for_enrollments=, :before_add_for_enrollments?, :before_add_for_skills, :before_add_for_skills=, :before_add_for_skills?, :before_add_for_users, :before_add_for_users=,
    :before_add_for_users?, :before_remove_for_categories, :before_remove_for_categories=, :before_remove_for_categories?, :before_remove_for_enrollments, :before_remove_for_enrollments=, :before_remo
    ve_for_enrollments?, :before_remove_for_skills, :before_remove_for_skills=, :before_remove_for_skills?, :before_remove_for_users, :before_remove_for_users=, :before_remove_for_users?, :blank?, :ca
    che_key, :cache_timestamp_format, :cache_timestamp_format?, :can_approve?, :can_deprecate?, :can_hide?, :can_publish?, :capture, :categories, :categories=, :category_ids, :category_ids=, :changed,
    :changed?, :changed_attributes, :changed_for_autosave?, :changes, :changes_applied, :class, :class_eval, :clear_aggregation_cache, :clear_association_cache, :clear_changes_information, :clear_dest
    roy_state, :clear_transaction_record_state, :clone, :clone_attribute_value, :column_for_attribute, :committed!, :concern, :connection_handler, :created?, :created_at, :created_at=, :created_at?, :
    created_at_before_type_cast, :created_at_change, :created_at_changed?, :created_at_was, :created_at_will_change!, :decrement, :decrement!, :deep_dup, :default_connection_handler, :default_connecti
    on_handler?, :default_scopes, :default_timezone, :define_singleton_method, :defined_enums, :defined_enums=, :defined_enums?, :delete, :deprecate, :deprecate!, :deprecate_transition, :deprecated?,
    :description, :description=, :description?, :description_before_type_cast, :description_change, :description_changed?, :description_was, :description_will_change!, :destroy, :destroy!, :destroyed?
    , :destroyed_by_association, :destroyed_by_association=, :display, :dump_schema_after_migration, :dup, :duplicable?, :enable_warnings, :encode_with, :enrollment_ids, :enrollment_ids=, :enrollments
    , :enrollments=, :enum_for, :eql?, :equal?, :error_on, :errors, :errors_on, :extend, :find_by_statement_cache, :find_by_statement_cache=, :find_by_statement_cache?, :fire_events, :fire_events!, :f
    ire_status_event, :force_clear_transaction_record_state, :freeze, :from_json, :from_xml, :frozen?, :gem, :handle, :handle=, :handle?, :handle_before_type_cast, :handle_change, :handle_changed?, :h
    andle_was, :handle_will_change!, :has_attribute?, :has_transactional_callbacks?, :hash, :hidden?, :hide, :hide!, :hide_transition, :html_safe?, :human_status_name, :id, :id=, :id?, :id_before_type
    _cast, :id_change, :id_changed?, :id_was, :id_will_change!, :in?, :include_root_in_json, :include_root_in_json=, :include_root_in_json?, :increment, :increment!, :init_with, :initialize_internals_
    callback, :initialize_state_machines, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_values, :instance_variable_defined?, :instance_variable_get, :instance_variable_names, :ins
    tance_variable_set, :instance_variables, :invalid?, :is_a?, :is_haml?, :itself, :kind_of?, :load_dependency, :lock!, :lock_optimistically, :lock_optimistically?, :locking_enabled?, :logger, :mark_
    for_destruction, :marked_for_destruction?, :method, :method_missing, :methods, :model_name, :name, :name=, :name?, :name_before_type_cast, :name_change, :name_changed?, :name_was, :name_will_chang
    e!, :nested_attributes_options, :nested_attributes_options?, :new_record?, :nil?, :no_touching?, :object_id, :organization, :organization=, :organization?, :organization_before_type_cast, :organiz
    ation_change, :organization_changed?, :organization_was, :organization_will_change!, :partial_writes, :partial_writes?, :perform_validations, :persisted?, :pluralize_table_names, :pluralize_table_
    names?, :populate_with_current_scope_attributes, :presence, :presence_in, :present?, :pretty_inspect, :pretty_print, :pretty_print_cycle, :pretty_print_inspect, :pretty_print_instance_variables, :
    previous_changes, :primary_key_prefix_type, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :publish, :publish!, :publish_transition, :published?, :query_attri
    bute, :quietly, :quietly_with_deprecation_silenced, :quietly_without_deprecation_silenced, :quoted_id, :raise_in_transactional_callbacks, :raise_record_invalid, :read_attribute, :read_attribute_be
    fore_type_cast, :read_attribute_for_serialization, :read_attribute_for_validation, :read_store_attribute, :readonly!, :readonly?, :record_timestamps, :record_timestamps=, :record_timestamps?, :rel
    oad, :remember_transaction_record_state, :remove_instance_variable, :require_dependency, :require_or_load, :reset_created_at!, :reset_description!, :reset_handle!, :reset_id!, :reset_name!, :reset
    _organization!, :reset_status!, :reset_updated_at!, :respond_to?, :respond_to_without_attributes?, :restore_attributes, :restore_created_at!, :restore_description!, :restore_handle!, :restore_id!,
    :restore_name!, :restore_organization!, :restore_status!, :restore_transaction_record_state, :restore_updated_at!, :rollback_active_record_state!, :rolledback!, :run_callbacks, :run_validations!,
    :sanitize_for_mass_assignment, :sanitize_forbidden_attributes, :save, :save!, :schema_format, :send, :serializable_hash, :set_transaction_state, :should, :should_not, :silence, :silence_stderr, :s
    ilence_stream, :silence_warnings, :singleton_class, :singleton_class?, :singleton_methods, :skill_ids, :skill_ids=, :skills, :skills=, :skip_time_zone_conversion_for_attributes, :skip_time_zone_co
    nversion_for_attributes?, :slice, :status, :status=, :status?, :status_before_type_cast, :status_change, :status_changed?, :status_event, :status_event=, :status_event_transition, :status_event_tr
    ansition=, :status_events, :status_name, :status_paths, :status_transitions, :status_was, :status_will_change!, :store_full_sti_class, :store_full_sti_class?, :suppress, :suppress_warnings, :syck_
    to_yaml, :table_name_prefix, :table_name_prefix?, :table_name_suffix, :table_name_suffix?, :taguri, :taguri=, :taint, :tainted?, :tap, :time_zone_aware_attributes, :timestamped_migrations, :to_enu
    m, :to_gid, :to_global_id, :to_json, :to_json_with_active_support_encoder, :to_json_without_active_support_encoder, :to_key, :to_model, :to_param, :to_partial_path, :to_query, :to_s, :to_sgid, :to
    _signed_global_id, :to_xml, :to_yaml, :to_yaml_properties, :to_yaml_style, :toggle, :toggle!, :touch, :transaction, :transaction_include_any_action?, :transaction_record_state, :trust, :try, :try!
    , :type_for_attribute, :unloadable, :untaint, :untrust, :untrusted?, :update, :update!, :update_attribute, :update_attributes, :update_attributes!, :update_column, :update_columns, :updated_at, :u
    pdated_at=, :updated_at?, :updated_at_before_type_cast, :updated_at_change, :updated_at_changed?, :updated_at_was, :updated_at_will_change!, :user_ids, :user_ids=, :users, :users=, :valid?, :valid
    ate, :validate!, :validate_associated_records_for_categories, :validate_associated_records_for_enrollments, :validate_associated_records_for_skills, :validate_associated_records_for_users, :valida
    tes_absence_of, :validates_acceptance_of, :validates_confirmation_of, :validates_exclusion_of, :validates_format_of, :validates_inclusion_of, :validates_length_of, :validates_numericality_of, :val
    idates_presence_of, :validates_size_of, :validates_with, :validation_context, :validation_context=, :with_lock, :with_options, :with_transaction_returning_status, :with_warnings, :write_store_attr
    ibute]

    View full-size slide

  85. @jmmastey
    RTFM? STFU.

    View full-size slide

  86. @jmmastey
    User.find_by_email_and_first_name

    View full-size slide

  87. @jmmastey
    User.find_by(email: ‘’,
    first_name: ‘’)

    View full-size slide

  88. @jmmastey
    㽩 /export/nonce (master) > rspec bomb_spec.rb
    Failures:
    1) MyObject works at all
    Failure/Error: expect(1).to eq(0)
    expected: 0
    got: 1
    … stacktrace …
    Finished in 0.00375 seconds (files took 0.29196 seconds to
    load)
    1 example, 1 failure
    Failed examples:
    rspec ./bomb_spec.rb:4 # MyObject works at all

    View full-size slide

  89. @jmmastey
    㽩 /export/nonce (master) > rspec bomb_spec.rb
    Failures:
    1) MyObject works at all
    Failure/Error: expect(1).to eq(0)
    expected: 0
    got: 1
    … stacktrace …
    Finished in 0.00375 seconds (files took 0.29196 seconds to
    load)
    1 example, 1 failure
    Failed examples:
    rspec ./bomb_spec.rb:4 # MyObject works at all

    View full-size slide

  90. @jmmastey
    㽩 /export/nonce (master) > rspec bomb_spec.rb
    Failures:
    1) MyObject works at all
    Failure/Error: expect(1).to eq(0)
    expected: 0
    got: 1
    … stacktrace …
    Finished in 0.00375 seconds (files took 0.29196 seconds to
    load)
    1 example, 1 failure
    Failed examples:
    rspec ./bomb_spec.rb:4 # MyObject works at all

    View full-size slide

  91. @jmmastey
    rspec ./bomb_spec.rb:4 # MyObject works at all

    View full-size slide

  92. @jmmastey
    @macguffin = MacGuffin.find(1)

    View full-size slide

  93. @jmmastey
    So what’s the point?

    View full-size slide

  94. @jmmastey
    “The world is filled with plenty of
    anguish — make your life goal not to
    add to it.”

    View full-size slide

  95. @jmmastey
    This is the mechanism for self
    documenting code

    View full-size slide

  96. @jmmastey
    The part where I ask you
    for things

    View full-size slide

  97. @jmmastey
    “So you wanna be a user experience designer”
    - Whitney Hess (http://bit.ly/1Hn1nQS)
    “The Design of Everyday Things” - Don Norman
    (I dunno, Amazon?)

    View full-size slide

  98. @jmmastey
    Thanks!
    Frustration Photo by jseliger2 (https://www.flickr.com/photos/91262622@N02/)
    Happy Kids by deepblue66 (https://www.flickr.com/photos/deepblue66/14503838952)
    Annotated code by Avdi Grimm (https://avdi.org)
    All icons from the NounProject (https://nounproject.com)

    View full-size slide