When you're writing a Ruby on Rails application, certain decisions seem appropriate and good at the beginning, but come back to bite you later on. In this talk, I list 20 different "traps," things that seem appealing, but which you should think twice before doing. The talk is based on my experience teaching Ruby and Rails classes to many people over the years, and was a presentation at the "Rails Israel 2012" conference held in Tel Aviv.
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Rails traps
1. Rails Traps
Reuven M. Lerner • reuven@lerner.co.il
International Rails Conference
November 12th, 2012 • Tel Aviv
2. Who am I?
• Web developer since 1993
• Linux Journal columnist since 1996
• Long-time developer/trainer/consultant in
Ruby, Rails, and PostgreSQL (and many
more)
3. What do I do?
• Web development, especially in Rails
• Teaching/training
• 4-day Ruby course in Herzliya next week
• Coaching/consulting
7. ... but it’s not worth the
long-term
consequences
8.
9. Rails traps
• Techniques that look like they’ll benefit us...
• ... but which will haunt us down the road
• There is some overlap these and “anti-
patterns,” a term that people use to
describe “design patterns that you should
avoid”
10.
11. Trap #1:
Always using Rails
• Rails is wonderful. Really, it is.
• But it’s not the only framework.
• (And Ruby isn’t the only language!)
• Use Rails because it’s the right framework
for the job
12. Alternatives
• My favorite: Sinatra
• Ridiculously easy to create Web apps, built
in Ruby, works with ERb and Haml (and
other gems we know and love), and
fantastic documentation
• But really, you should use what you know, if
it gets the job done. (Even PHP...)
13. Trap #2:
Ignoring Rails’ opinions
• One of the best things about Rails is that
it’s opinionated
• Files go in specific places
• MVC split is (fairly) obvious
• ActiveRecord naming is pre-set
• If your opinions differ, you may have trouble
15. Opinions may differ...
but not in Rails
• Yes, you can configure Rails to conform to
your opinions
• But in doing so, you’re likely to encounter
pain — now, and in the future
• Different parts of Rails make the same
assumptions, so expect extended pain
• Stick with the defaults as much as possible
16. Some examples
• Always have a synthetic primary key, and
always call it “id”
• Naming conventions for tables, classes
• Fat models and skinny controllers
• DRY things up with filters, callbacks
17. Trap #3:
Ignoring the logs
• Logs are a developer’s best friend
• Is there a problem? Check the logs!
• Having a problem? Write to the log!
• (Check the database log, too!)
• Using a debugger should be your last
resort, not your first one
18. Trap #4:
Ignoring the console
• The console (especially with Pry) is one of
the most useful parts of Rails
• Don’t ignore it, or think that it’s primitive
• The console is a critical part of my
development and debugging process
• It helps me “feel” my way through the app
19. Trap #5:
Not being RESTful
• For years, Rails has encouraged us to use
RESTful resources
• You don’t have to; config/routes.rb still
contains the following line (in a comment):
match ':controller(/:action(/:id))(.:format)'
• Maybe you don’t have to be RESTful. But life
is far easier when you accept REST.
20. Being RESTful
• Think “resources” rather than controllers
• Resources should be nouns, and actions
operate on those nouns
• Those actions can usually be mapped onto
the standard seven methods
21. What REST gives you
• Convention over configuration
• Ease of maintenance/understanding
• Routes, forms, and helpers that just work
• Scaffolds (yes, I like scaffolds)
• Forces you to think (and rethink) the
structure of your application, which an
definitely improve it
22. Trap #6:
Only being RESTful
• REST is a technique, not a religion
• If you have non-RESTful controller actions,
you are not an evil person!
• (Well, not too evil)
• Add new member or collection methods
when you need them — but not more
often than that
23. Trap #7:
Self authentication
• Rails doesn’t have a built-in authentication
system. (This drives me crazy.)
• There are gems (e.g., Devise) that do a
fantastic job, and which have been used and
tested by many sites
• Security is hard to get right!
• Please don’t roll your own. Use a gem.
24. Trap #8:
Premature optimization
• Cache everything!
• Index everything!
• Get huge servers!
• Use bit masks in the database to avoid
overloading the system!
25. Think about Ruby
• Ruby isn’t optimized for program speed, but
rather for programmer speed
• That is an important competitive advantage
• Computers are cheap, but people are not
• First make sure your software is
maintainable. Then worry whether it’s fast.
• And then benchmark before optimizing!
26. Trap #9:
DB operations in Ruby
• Of course, there are some places where
you should consider speed
• Databases are really smart and efficient
about handling data
• Plus, the data is far smaller as tuples than as
ActiveRecord objects
• Let the database do the serious data lifting
27. Don’t do this
• Please don’t do this:
Person.all.select {|p| p.age > 40}
• This will be incredibly slow.
• Serious databases (e.g., PostgreSQL) let you
create server-side functions and views, which
can manipulate the data before it ever gets
to Ruby, at database speeds
28. Trap #10:
Different databases
• I often see people using SQLite in
development, and PostgreSQL in
production
• Don’t do this, especially if you’re using an
open-source database in production
• Databases are different — different data
types, integrity solutions, and transactional
behavior.
29. Trap #11:
NoSQL or Bust
• I hear many people say that SQL is slow,
bad, complex, or otherwise
• All of their problems will disappear if they
switch to NoSQL!
• Really?
• That’s like saying, “Hashes are great, so I’ll
use them instead of arrays.”
30. Trap #12:
The cloud! The cloud!
• Cloud solutions are great ...
• ... in certain circumstances.
• I’m using Heroku with three of my clients
right now, and I’m very pleased with their
product and support.
• But that doesn’t mean it’s a better, or even
cheaper, option for everyone out there.
31. My cloudy questions
• How much direct control do I want/need
over my company’s servers?
• Is the cost of cloud services lower than the
cost of configuring and maintaining these
myself?
• How likely am I to have to scale up
massively, at a moment’s notice?
32. Trap #13:
Long controller actions
• It’s so tempting! You want to do so much
in your controller action.
• But the user doesn’t want to wait for your
e-mail to be sent.
• Use Resque, Delayed Job, or whatever you
want... but put such things in the
background, and answer the user ASAP.
33. Trap #14:
No DB constraints
• Is your Rails application the only program
that will ever touch the database?
• (Answer: Almost certainly not.)
• So why are your constraints only in Rails?
• A good database will let you specify not
only NOT NULLs, but also valid values to
protect your data
34. Trap #15:
Lack of callbacks, filters
• Callbacks (on ActiveRecord models) and
filters (on controller actions) let you
separate the “real work” from the less-
important work
• Keep code short, readable, and DRY
35. Trap #16:
Not using “lib”
• We talk a lot about MVC in Rails
• But don’t forget the “lib” directory
• It’s a great place to place modules and
classes that are used across your app, or
that aren’t specifically tied to MVC
• (Remember that lib isn’t automatically in
the load path, as of Rails 3.x.)
36. Trap #17:
Modifying migrations
• Migrations are one of my favorite parts of
Rails.
• Migrations are reversible: If you make a
mistake, you can migrate down, change the
migration file, and then migrate up.
• Only do this before you have shared your
migration with other people
37. Why not?
• Once you have pushed your migration,
someone might have applied them
• Maybe you know to migrate backward,
revert the old version, git pull, and then
migrate forward... but does everyone?
38. Trap #18:
Using class variables
• When I teach Ruby, everyone loves class
variables! (They also love to call them
“static variables.”)
• There is a place for class variables, but it’s a
very limited one:
• Data that needs to be shared across all
instances of a class
39. Class variable
alternatives
• Class-level constants
• Class methods
• A module with methods or constants
• A model (for some types of constants)
40. And don’t forget
• Class variables vs. class instance variables
• This is where your understanding of the
Ruby object model really comes in handy
41. class Confusion
@x = 11 # class instance variable
@@x = 22 # class variable
def initialize
@x = 999 # instance variable
end
def return_x
@x # returns 999
end
def self.return_x
@x # returns 11
end
def return_double_x
@@x # returns 22
end
def self.return_double_x
@@x # returns 22
end
end
42. class Confusion
@x = 11 # class instance variable
@@x = 22 # class variable
def initialize
@x = 999 # instance variable
end
def return_x
@x # returns 999
end
def self.return_x
@x # returns 11
end
def return_double_x
@@x # returns 22
end
def self.return_double_x
@@x # returns 22
end
end
43. class Confusion
@x = 11 # class instance variable
@@x = 22 # class variable
def initialize
@x = 999 # instance variable
end
def return_x
@x # returns 999
end
def self.return_x
@x # returns 11
end
def return_double_x
@@x # returns 22
end
def self.return_double_x
@@x # returns 22
end
end
44. class Confusion
@x = 11 # class instance variable
@@x = 22 # class variable
def initialize
@x = 999 # instance variable
end
def return_x
@x # returns 999
end
def self.return_x
@x # returns 11
end
def return_double_x
@@x # returns 22
end
def self.return_double_x
@@x # returns 22
end
end
45. class Confusion
@x = 11 # class instance variable
@@x = 22 # class variable
def initialize
@x = 999 # instance variable
end
def return_x
@x # returns 999
end
def self.return_x
@x # returns 11
end
def return_double_x
@@x # returns 22
end
def self.return_double_x
@@x # returns 22
end
end
46. class Confusion
@x = 11 # class instance variable
@@x = 22 # class variable
def initialize
@x = 999 # instance variable
end
def return_x
@x # returns 999
end
def self.return_x
@x # returns 11
end
def return_double_x
@@x # returns 22
end
def self.return_double_x
@@x # returns 22
end
end
47. class Confusion
@x = 11 # class instance variable
@@x = 22 # class variable
def initialize
@x = 999 # instance variable
end
def return_x
@x # returns 999
end
def self.return_x
@x # returns 11
end
def return_double_x
@@x # returns 22
end
def self.return_double_x
@@x # returns 22
end
end
48. Trap #19:
Cool technology
• Don’t use technology because it’s cool.
Use technology because it gets the job
done, and helps the business.
• So it might be cool to use a pub-sub system
written in a .NET port of Clojure that
transfers XML using EBCDIC...
• ... but is it necessary?
Is it the easiest and
best solution? Is it maintainable?
49. Choosing technologies
• My preference is to use things that are
proven, stable, and a little boring
• (Like me)
• The stability and viability of a project is far
more important than the latest cool hack
• (Which I’ll install on my own machine to
play with, but not use on clients’ projects)
50. Trap #20:
Not learning, improving
• You cannot survive in our business without
learning new techniques all of the time
• The Ruby and Rails worlds move especially
fast, with new gems released all of the time
51. What to do?
• Podcasts (Ruby Rogues, Ruby show, Ruby 5)
• Newsletters (Ruby Weekly)
• Blogs
• Books (remember those?)
• Learn other languages! You can pick up a
lot of ideas and techniques from them
52. Bonus trap:
Not enjoying yourself
• Ruby and Rails make programming fun
• If you’re not having fun, then maybe you’re
not doing it right
• (Or go join the wild party that I hear .NET
developers are having!)
53. Thanks!
(Any questions?)
reuven@lerner.co.il
http://www.lerner.co.il/
054-496-8405
“reuvenlerner” on Skype/AIM