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

Mantendo a sua aplicação Rails organizada

mateusg
October 10, 2015

Mantendo a sua aplicação Rails organizada

Quando criamos uma nova app com Rails, o framework já nos traz uma série de convenções de nomenclatura e estrutura que nos auxilia a mantê-la organizada. Entretanto, conforme ela cresce e vai se tornando mais complexa, torna-se cada vez mais difícil manter o codebase organizado e de fácil compreensão.Nessa palestra serão apresentadas algumas ferramentas e práticas para auxiliar nesse desafio e manter o código da sua app Rails mais legível e manutenível.

* Slides adaptados de https://speakerdeck.com/fnando/criando-aplicacoes-mais-faceis-de-manter-com-ruby-on-rails

mateusg

October 10, 2015
Tweet

Other Decks in Programming

Transcript

  1. As views se tornam complexas Começa a se acumular lógica

    de formatação, de geração de classes CSS e muitas condicionais.
  2. Os controllers se tornam complexos Modificar o comportamento de uma

    action envolve muito esforço. Testar e fazer manutenção nos testes existentes, mais ainda.
  3. Os models se tornam complexos Classes cada vez maiores, acumulando

    muita lógica de negócio, mais responsabilidade do que deveriam ter.
  4. Será que o problema está no Rails? Ou será que

    é preciso mudar a forma como o código é escrito, assim como a arquitetura da aplicação?
  5. Nomenclatura deve ser levada a sério Você pode achar que

    o nome de uma classe está perfeito, mas ainda assim ele pode ser interpretado da forma errada.
  6. –Uncle Bob Martin, Clean Code “Fazer o seu código legível

    é tão importante quanto fazê-lo executável.”
  7. Explícito > Implícito Só o diretório em que a classe

    se encontra não é suficiente para indicar o que ela é ou a qual contexto ela pertence.
  8. –DHH “Se você se esforçar para ser claro na nomenclatura,

    raramente precisará escrever comentários explicativos.” https://signalvnoise.com/posts/3250-clarity-over-brevity-in-variable-and-method- names
  9. def  turn_into_an_outside_subscriber_if_accesses_revoked      update_attribute(:outside_subscriber,  true)  if  accesses_revoked?   end

      def  shift_records_upward_starting_at_position(position)      positioned_records.update_all  "position  =  position  -­‐  1",          ["position  >=  ?",  position]   end   def  accesses_revoked?      accesses.blank?   end  
  10. Testes automatizados são essenciais Além de facilitarem a compreensão do

    código, dão mais segurança para evoluí-lo e fazer manutenção.
  11. Separe os testes unitários por método Estruturar os testes hierarquicamente

    em classe => método => contextos torna a leitura e a compreensão mais rápida.
  12. #  app/models/webhook.rb   class  Webhook  <  ActiveRecord::Base      def

     enable!          update_column  :enabled,  true      end      def  disable!          update_column  :enabled,  false      end   end  
  13. describe  Webhook  do      subject(:webhook)  {  create  :webhook,  enabled:

     false  }      describe  'enabling  webhook'  do          it  'updates  enabled  to  true'  do              webhook.enable!                            expect(webhook.reload.enabled).to  be  true          end      end      describe  'disabling  webhook'  do          #...      end      describe  'deleting  webhook'  do          #  ...      end   end  
  14. describe  Webhook  do      subject(:webhook)  {  create  :webhook,  enabled:

     false  }      describe  'enabling  webhook'  do          it  'updates  enabled  to  true'  do              webhook.enable!                            expect(webhook.reload.enabled).to  be  true          end      end      describe  'disabling  webhook'  do          #...      end      describe  'deleting  webhook'  do          #  ...      end   end  
  15. #  spec/models/webhook_spec.rb   describe  Webhook  do      describe  '#enable!'

     do          subject(:webhook)  {  create  :webhook,  enabled:  false  }          it  'updates  enabled  to  true'  do              webhook.enable!                            expect(webhook.reload.enabled).to  be  true          end      end      describe  '#disable!'  do          subject(:webhook)  {  create  :webhook,  enabled:  false  }          it  'updates  enabled  to  false'  do              webhook.disable!                            expect(webhook.reload.enabled).to  be  false          end      end   end  
  16. #  spec/models/webhook_spec.rb   describe  Webhook  do      describe  '#enable!'

     do          subject(:webhook)  {  create  :webhook,  enabled:  false  }          it  'updates  enabled  to  true'  do              webhook.enable!                            expect(webhook.reload.enabled).to  be  true          end      end      describe  '#disable!'  do          subject(:webhook)  {  create  :webhook,  enabled:  false  }          it  'updates  enabled  to  false'  do              webhook.disable!                            expect(webhook.reload.enabled).to  be  false          end      end   end  
  17. –Kent Beck “Sou pago para escrever código que funciona, não

    testes, então minha filosofia é testar o suficiente para atingir um certo nível de confiança.”
  18. Dê preferência aos testes unitários Testes unitários são mais rápidos

    e simples, portanto são mais fáceis de serem mantidos.
  19. Testes não devem ser DRY A legibilidade de um teste

    não pode ser prejudicada para deixá- lo DRY. Testes devem ser de fácil entendimento, já que também têm papel de documentação do comportamento do código.
  20. [:phone_number,  :cellphone_number].each  do  |attr|      it  {  is_expected.to  validate_presence_of(attr)

     }      it  {  is_expected.to  validate_format_of(attr)  }   end   http://betterspecs.org
  21. [:phone_number,  :cellphone_number].each  do  |attr|      it  {  is_expected.to  validate_presence_of(attr)

     }      it  {  is_expected.to  validate_format_of(attr)  }   end   Gerando testes dinamicamente http://betterspecs.org
  22. [:phone_number,  :cellphone_number].each  do  |attr|      it  {  is_expected.to  validate_presence_of(attr)

     }      it  {  is_expected.to  validate_format_of(attr)  }   end   Gerando testes dinamicamente http://betterspecs.org
  23. [:phone_number,  :cellphone_number].each  do  |attr|      it  {  is_expected.to  validate_presence_of(attr)

     }      it  {  is_expected.to  validate_format_of(attr)  }   end   Gerando testes dinamicamente it  {  is_expected.to  validate_presence_of(:phone_number)  }   it  {  is_expected.to  validate_presence_of(:cellphone_number)  }   it  {  is_expected.to  validate_format_of(:phone_number)  }   it  {  is_expected.to  validate_format_of(:cellphone_number)  }   ! http://betterspecs.org
  24. describe  'List'  do      describe  '#clear'  do    

         before  {  queue.clear  }          it  'removes  all  items'  do              expect(queue.items).to  be_empty          end      end   end  
  25. describe  'List'  do      describe  '#clear'  do    

         before  {  queue.clear  }          it  'removes  all  items'  do              expect(queue.items).to  be_empty          end      end   end   DRY!
  26.        end          it  '...'

     do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  'removes  all  items'  do              expect(queue.items).to  be_empty          end      end   end  
  27.        end          it  '...'

     do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  'removes  all  items'  do              expect(queue.items).to  be_empty          end      end   end   O que está sendo testado?
  28.        end          it  '...'

     do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  'removes  all  items'  do              expect(queue.items).to  be_empty          end      end   end   O que está sendo testado?
  29.        it  '...'  do        

     end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  'removes  all  items'  do              list.clear              expect(list.items).to  be_empty          end      end   end  
  30.        it  '...'  do        

     end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  '...'  do          end          it  'removes  all  items'  do              list.clear              expect(list.items).to  be_empty          end      end   end   !
  31. Refatore, refatore, refatore Busque deixar os componentes da sua aplicação

    mais enxutos e livres de responsabilidades que não os pertencem.
  32. O Rails Way tem se mostrado insuficiente para aplicações complexas

    Fuja das convenções sempre que for conveniente, mas avalie com cuidado as suas decisões.
  33. Abstrações de mais ou de menos? O excesso de abstrações

    pode ser tão prejudicial quanto a falta de abstrações.
  34. Extraia lógica das views Views não devem ter nada além

    de loops, forms enxutos e lógicas simples de exibição.
  35. / app/views/payments/index.html.haml -­‐  @payments.each  do  |payment|      %tr{class:  payment.paid?

     ?  'paid'  :  ''}          %td              =  l  payment.paid_at,  format:  :short          %td              =  number_to_currency  payment.value          %td              =  number_to_percentage  payment.discount          %td              =  number_to_currency  payment.recharge_value          %td              =  number_to_currency  payment.total          %td              =  link_to  invoice_path(payment.invoice)  if   payment.invoice.present?  &&  current_user.has_role?  :manager          %td              =  t("payments.methods.#{payment.method}")          %td              =  payment.automatic?  ?  'Sim'  :  'Não'  
  36. / app/views/payments/index.html.haml -­‐  @payments.each  do  |payment|      %tr{class:  payment.paid?

     ?  'paid'  :  ''}          %td              =  l  payment.paid_at,  format:  :short          %td              =  number_to_currency  payment.value          %td              =  number_to_percentage  payment.discount          %td              =  number_to_currency  payment.recharge_value          %td              =  number_to_currency  payment.total          %td              =  link_to  invoice_path(payment.invoice)  if   payment.invoice.present?  &&  current_user.has_role?  :manager          %td              =  t("payments.methods.#{payment.method}")          %td              =  payment.automatic?  ?  'Sim'  :  'Não'  
  37. / app/views/payments/index.html.haml -­‐  @payments.each  do  |payment|      %tr{class:  payment_status_class(payment)}

             %td              =  l  payment.paid_at,  format:  :short          %td              =  number_to_currency  payment.value          %td              =  number_to_percentage  payment.discount          %td              =  number_to_currency  payment.recharge_value          %td              =  number_to_currency  payment.total          %td              =  invoice_link  payment,  current_user          %td              =  t("payments.methods.#{payment.method}")          %td              =  automatic_label(payment)  
  38. / app/views/payments/index.html.haml -­‐  @payments.each  do  |payment|      %tr{class:  payment_status_class(payment)}

             %td              =  l  payment.paid_at,  format:  :short          %td              =  number_to_currency  payment.value          %td              =  number_to_percentage  payment.discount          %td              =  number_to_currency  payment.recharge_value          %td              =  number_to_currency  payment.total          %td              =  invoice_link  payment,  current_user          %td              =  t("payments.methods.#{payment.method}")          %td              =  automatic_label(payment)   Injeção de dependência :(
  39. #  app/controllers/payments_controller.rb   def  index      @payments  =  Payment.page(params[:page]).decorate

      end   #  Gemfile   gem  'draper'   https://github.com/drapergem/draper
  40. #  app/controllers/payments_controller.rb   def  index      @payments  =  Payment.page(params[:page]).decorate

      end   #  Gemfile   gem  'draper'   https://github.com/drapergem/draper
  41. / app/views/payments/index.html.haml -­‐  @payments.each  do  |payment|      %tr{class:  payment.paid?

     ?  'paid'  :  ''}          %td              =  l  payment.paid_at,  format:  :short          %td              =  number_to_currency  payment.value          %td              =  number_to_percentage  payment.discount          %td              =  number_to_currency  payment.recharge_value          %td              =  number_to_currency  payment.total          %td              =  link_to  invoice_path(payment.invoice)  if   payment.invoice.present?  &&  current_user.has_role?  :manager          %td              =  t("payments.methods.#{payment.method}")          %td              =  payment.automatic?  ?  'Sim'  :  'Não'  
  42. / app/views/payments/index.html.haml -­‐  @payments.each  do  |payment|      %tr{class:  payment.paid?

     ?  'paid'  :  ''}          %td              =  l  payment.paid_at,  format:  :short          %td              =  number_to_currency  payment.value          %td              =  number_to_percentage  payment.discount          %td              =  number_to_currency  payment.recharge_value          %td              =  number_to_currency  payment.total          %td              =  link_to  invoice_path(payment.invoice)  if   payment.invoice.present?  &&  current_user.has_role?  :manager          %td              =  t("payments.methods.#{payment.method}")          %td              =  payment.automatic?  ?  'Sim'  :  'Não'  
  43. / app/views/payments/index.html.haml -­‐  @payments.each  do  |payment|      %tr{class:  payment.status_class}

             %td              =  payment.paid_at          %td              =  payment.formatted_value          %td              =  payment.formatted_discount          %td              =  payment.formatted_recharge_value          %td              =  payment.formatted_total          %td              =  payment.invoice_link          %td              =  payment.method_label          %td              =  payment.automatic_label  
  44. / app/views/payments/index.html.haml -­‐  @payments.each  do  |payment|      %tr{class:  payment.status_class}

             %td              =  payment.paid_at          %td              =  payment.formatted_value          %td              =  payment.formatted_discount          %td              =  payment.formatted_recharge_value          %td              =  payment.formatted_total          %td              =  payment.invoice_link          %td              =  payment.method_label          %td              =  payment.automatic_label  
  45. #  app/decorators/payment_decorator.rb   class  PaymentDecorator  <  Draper::Decorator      def

     paid_at          l  object.paid_at,  format:  :short      end      def  status_class          object.paid?  ?  'paid'  :  nil      end      def  formatted_value          number_to_currency  object.value      end      def  formatted_discount          number_to_percentage  object.discount      end      def  formatted_recharge_value          number_to_currency  object.recharge_value      end      def  formatted_total          number_to_currency  object.total      end      def  invoice_link          if  object.invoice.present?  &&  current_user.has_role?  :manager              link_to  invoice_path  object.invoice          end      end      def  method_label          t  object.method,  scope:  'payments.methods'      end      def  automatic_label          t  object.automatic?,  scope:  'yes_or_no'      end   end  
  46. #  app/decorators/payment_decorator.rb   class  PaymentDecorator  <  Draper::Decorator      def

     paid_at          l  object.paid_at,  format:  :short      end      def  status_class          object.paid?  ?  'paid'  :  nil      end      def  formatted_value          number_to_currency  object.value      end      def  formatted_discount          number_to_percentage  object.discount      end      def  formatted_recharge_value          number_to_currency  object.recharge_value      end      def  formatted_total          number_to_currency  object.total      end      def  invoice_link          if  object.invoice.present?  &&  current_user.has_role?  :manager              link_to  invoice_path  object.invoice          end      end      def  method_label          t  object.method,  scope:  'payments.methods'      end      def  automatic_label          t  object.automatic?,  scope:  'yes_or_no'      end   end  
  47. #  app/decorators/payment_decorator.rb   class  PaymentDecorator  <  Draper::Decorator      def

     paid_at          l  object.paid_at,  format:  :short      end      def  status_class          object.paid?  ?  'paid'  :  nil      end      def  formatted_value          number_to_currency  object.value      end      def  formatted_discount          number_to_percentage  object.discount      end      def  formatted_recharge_value          number_to_currency  object.recharge_value      end      def  formatted_total          number_to_currency  object.total      end      def  invoice_link          if  object.invoice.present?  &&  current_user.has_role?  :manager              link_to  invoice_path  object.invoice          end      end      def  method_label          t  object.method,  scope:  'payments.methods'      end      def  automatic_label          t  object.automatic?,  scope:  'yes_or_no'      end   end  
  48. class  SignupsController  <  ApplicationController      def  create    

         @signup  =  Signup.new(params[:signup])          if  @signup.save              redirect_to  dashboard_path          else              render  "new"          end      end   end  
  49. class  SignupsController  <  ApplicationController      def  create    

         @signup  =  Signup.new(params[:signup])          if  @signup.save              redirect_to  dashboard_path          else              render  "new"          end      end   end  
  50. class  SignupsController  <  ApplicationController      def  create    

         @signup  =  Signup.new(params[:signup])          if  @signup.save              redirect_to  dashboard_path          else              render  "new"          end      end   end   Delega para o form object
  51. class  Signup      include  Virtus      extend  ActiveModel::Naming

         include  ActiveModel::Conversion      include  ActiveModel::Validations      attr_reader  :user,  :company      attribute  :name,  String      attribute  :company_name,  String      attribute  :email,  String      validates  :email,  presence:  true      #  ...      def  save          if  valid?              persist!;  true          else              false          end      end            private      def  persist!          @company  =  Company.find_or_create_by!(name:  company_name)          @user  =  @company.users.create!(name:  name,  email:  email)      end   end   https://github.com/solnic/virtus
  52. class  Signup      include  Virtus      extend  ActiveModel::Naming

         include  ActiveModel::Conversion      include  ActiveModel::Validations      attr_reader  :user,  :company      attribute  :name,  String      attribute  :company_name,  String      attribute  :email,  String      validates  :email,  presence:  true      #  ...      def  save          if  valid?              persist!;  true          else              false          end      end            private      def  persist!          @company  =  Company.find_or_create_by!(name:  company_name)          @user  =  @company.users.create!(name:  name,  email:  email)      end   end   https://github.com/solnic/virtus
  53. class  Signup      include  Virtus      extend  ActiveModel::Naming

         include  ActiveModel::Conversion      include  ActiveModel::Validations      attr_reader  :user,  :company      attribute  :name,  String      attribute  :company_name,  String      attribute  :email,  String      validates  :email,  presence:  true      #  ...      def  save          if  valid?              persist!;  true          else              false          end      end            private      def  persist!          @company  =  Company.find_or_create_by!(name:  company_name)          @user  =  @company.users.create!(name:  name,  email:  email)      end   end   https://github.com/solnic/virtus
  54. class  Signup      include  Virtus      extend  ActiveModel::Naming

         include  ActiveModel::Conversion      include  ActiveModel::Validations      attr_reader  :user,  :company      attribute  :name,  String      attribute  :company_name,  String      attribute  :email,  String      validates  :email,  presence:  true      #  ...      def  save          if  valid?              persist!;  true          else              false          end      end            private      def  persist!          @company  =  Company.find_or_create_by!(name:  company_name)          @user  =  @company.users.create!(name:  name,  email:  email)      end   end   https://github.com/solnic/virtus
  55. class  Signup      include  Virtus      extend  ActiveModel::Naming

         include  ActiveModel::Conversion      include  ActiveModel::Validations      attr_reader  :user,  :company      attribute  :name,  String      attribute  :company_name,  String      attribute  :email,  String      validates  :email,  presence:  true      #  ...      def  save          if  valid?              persist!;  true          else              false          end      end            private      def  persist!          @company  =  Company.find_or_create_by!(name:  company_name)          @user  =  @company.users.create!(name:  name,  email:  email)      end   end   https://github.com/solnic/virtus
  56. class  Signup      include  Virtus      extend  ActiveModel::Naming

         include  ActiveModel::Conversion      include  ActiveModel::Validations      attr_reader  :user,  :company      attribute  :name,  String      attribute  :company_name,  String      attribute  :email,  String      validates  :email,  presence:  true      #  ...      def  save          if  valid?              persist!;  true          else              false          end      end            private      def  persist!          @company  =  Company.find_or_create_by!(name:  company_name)          @user  =  @company.users.create!(name:  name,  email:  email)      end   end   https://github.com/solnic/virtus
  57. =  form_for  @signup,  url:  signup_path  do  |f|      =

     f.label  :company_name      =  f.text_field  :company_name      =  f.label  :name      =  f.text_field  :name      =  f.label  :email      =  f.text_field  :email  
  58. =  form_for  @signup,  url:  signup_path  do  |f|      =

     f.label  :company_name      =  f.text_field  :company_name      =  f.label  :name      =  f.text_field  :name      =  f.label  :email      =  f.text_field  :email  
  59. Extraia lógica dos models Skinny Controllers, Fat Models é conversa

    pra boi dormir. Models não são o lugar responsável por fazer TUDO sobre o seu domínio.
  60. –Bryan Helmkamp, Code Climate “Any application with an app/ concerns

    directory is concerning.” http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat- activerecord-models/
  61. module  UserAssociations      extend  ActiveSupport::Concern      included  do

             has_many  :contacts,  dependent:  :destroy          has_one  :configuration,  dependent:  :destroy          #  has_many            #  has_many            #  has_many            #  belongs_to          #  has_many            #  has_one            #  has_many            #  has_one            #  has_many            #  has_one        end   end   class  User  <  ActiveRecord::Base      include  UserAssociations      validates  :contacts,  presence:  true      validates  :email,  :name,  presence:  true   end  
  62. Apesar de parecer que o código está organizado por cima,

    o model continua poluído É como dizer que um quarto foi arrumado depois pegar todas as roupas espalhadas, jogar de qualquer jeito em 6 gavetas e fechá-las.
  63. #  app/models/user.rb   class  User      has_many  :contacts  

       after_save  :import_contacts,  if:  :csv_file      def  import_contacts          Users::ContactsCSVImporter.import  csv_file,  user_id:  id      end   end   #  app/services/users/contacts_csv_importer.rb   module  Users      class  ContactsCSVImporter          def  self.import(csv_file,  options  =  {})              CSV.foreach(csv_file,  col_sep:  ';')  do  |row|                  #  ...                  Contact.create!(name:  name,  email:  email,  phone:  phone,   user_id:  options[:user_id])              end          end      end   end  
  64. #  app/models/user.rb   class  User      has_many  :contacts  

       after_save  :import_contacts,  if:  :csv_file      def  import_contacts          Users::ContactsCSVImporter.import  csv_file,  user_id:  id      end   end   #  app/services/users/contacts_csv_importer.rb   module  Users      class  ContactsCSVImporter          def  self.import(csv_file,  options  =  {})              CSV.foreach(csv_file,  col_sep:  ';')  do  |row|                  #  ...                  Contact.create!(name:  name,  email:  email,  phone:  phone,   user_id:  options[:user_id])              end          end      end   end  
  65. #  app/models/user.rb   class  User      has_many  :contacts  

       after_save  :import_contacts,  if:  :csv_file      def  import_contacts          Users::ContactsCSVImporter.import  csv_file,  user_id:  id      end   end   #  app/services/users/contacts_csv_importer.rb   module  Users      class  ContactsCSVImporter          def  self.import(csv_file,  options  =  {})              CSV.foreach(csv_file,  col_sep:  ';')  do  |row|                  #  ...                  Contact.create!(name:  name,  email:  email,  phone:  phone,   user_id:  options[:user_id])              end          end      end   end  
  66. #  app/models/user.rb   class  User      has_many  :contacts  

       after_save  :import_contacts,  if:  :csv_file      def  import_contacts          Users::ContactsCSVFile.import  csv_file,  user_id:  id      end   end   #  app/services/users/contacts_csv_importer.rb   module  Users      class  ContactsCSVFile          def  self.import(csv_file,  options  =  {})              CSV.foreach(csv_file,  col_sep:  ';')  do  |row|                  #  ...                  Contact.create!(name:  name,  email:  email,  phone:  phone,   user_id:  options[:user_id])              end          end      end   end  
  67. #  app/models/user.rb   class  User      has_many  :contacts  

       after_save  :import_contacts,  if:  :csv_file      def  import_contacts          Users::ContactsCSVFile.import  csv_file,  user_id:  id      end   end   #  app/services/users/contacts_csv_importer.rb   module  Users      class  ContactsCSVFile          def  self.import(csv_file,  options  =  {})              CSV.foreach(csv_file,  col_sep:  ';')  do  |row|                  #  ...                  Contact.create!(name:  name,  email:  email,  phone:  phone,   user_id:  options[:user_id])              end          end      end   end   Importer.import => CSVFile.import !
  68. Refactoring é sempre melhor do que rewrite Melhorar a qualidade

    do seu projeto incrementalmente é a melhor saída.
  69. Adapte a estrutura de diretórios A estrutura de diretórios deve

    seguir as suas necessidades, não fique preso ao esquema default do Rails.
  70. Mantenha as dependências atualizadas Uma aplicação que não tem as

    dependências atualizadas é uma aplicação aberta à todos os tipos de problemas.
  71. Sempre use a última versão estável do Rails Atualizar o

    framework não deve ser algo tão complicado, a comunidade sempre publica guias passo-a-passo quando sai novas versões.
  72. Atualize também para a última versão estável do Ruby Bug

    fixes, falhas de seguranças corrigidas e novas funcionalidades.
  73. Procure diminuir a quantidade de dependências Por que adicionar uma

    dependência complexa quando você pode implementar a mesma funcionalidade em poucas linhas de código?
  74. Avalie cuidadosamente os prós e contras de cada decisão Não

    siga nada sem saber quais as implicações de tal arquitetura ou pattern. 1.
  75. Não comece com uma arquitetura complexa Não siga nada sem

    saber quais as implicações de tal arquitetura ou pattern. 2.
  76. Sua experiência é mais importante Não é porque funcionou para

    outras pessoas que vai funcionar com você. Experimente! 3.
  77. Pensar fora da caixa é essencial O Rails se estabeleceu

    por sugerir padrões, mas os tempos mudam, não fique preso a este pensamento. 4.
  78. Bom design é melhor que qualquer arquitetura Não adianta redefinir

    a arquitetura se no fim o código continuar ruim. 5.