Ruby and Rails: The Bad Parts
Volodymyr Melnyk
Ruby On, Kharkiv
2015
Slide 2
Slide 2 text
1 minute of self-
advertisement
• Software craftsman with ~7
experience (~5y experience in
Ruby).
• Atheist
• Libertarian and anarcho-
capitalist.
• Scientist and skeptic.
• A man with business /
economics mind.
Slide 3
Slide 3 text
Ruby’s Good Parts
• It has high abilities in meta-
programming
• It’s good for Domain-Specific
Languages
• It allows to write prototypes
very fast
• It has a huge ecosystem with
tons of gems and development
tools.
Slide 4
Slide 4 text
Ruby’s Bad Parts or why Ruby isn’t brilliant
Slide 5
Slide 5 text
Ruby isn’t Static-
typed
Ruby has no typed signatures.
This means that you need to do
all type checks manually.
x = 5
y = “string"
lambda = ->{ x + y }
# you will not get an
# error until execution
# next code line:
lambda.call
Slide 6
Slide 6 text
Ruby has an unclear
concept of Module
In Ruby a module is a class
which can’t have instances and
can be included into another
class or another class can be
extended with it.
Slide 7
Slide 7 text
module InstanceMethods; end
module ClassMethods; end
class C
include InstanceMethods
extend ClassMethods
end
C.ancestors
#=> [C, InstanceMethods, Object, Kernel, BasicObject]
C.singleton_class.ancestors
#=> [
# #, ClassMethods, #,
# , Class, Module, Object, Kernel,
# BasicObject
# ]
Slide 8
Slide 8 text
Proc & Lambda
In Ruby we have a Proc class
and two types of its instances
(it’s strange), but actually we
need only one of them (as for me,
I prefer lambdas).
Slide 9
Slide 9 text
p1 = Proc.new { |a, b| a + b }
p2 = Proc.new { |a, b| return a + b }
l1 = ->(a, b) { a + b }
l2 = ->(a, b) { return a + b }
p1.class # Proc
l1.class # Proc
p1.lambda? # false
l1.lambda? # true
def mtd(pobj, a, b = nil)
args = b ? [a, b] : [a]
puts "method code 1"
puts pobj.call *args
puts "method code 2"
end
Delegator
class SimpleDelegator < Delegator
def initialize obj
super
@delegate_sd_obj = obj
end
def __getobj__
@delegate_sd_obj
end
def __setobj__ obj
@delegate_sd_obj = obj
end
end
Slide 15
Slide 15 text
Alternate syntax
Ruby has a lot of alternate
syntaxes for different things.
Because of that Ruby code can
look very differently.
Slide 16
Slide 16 text
{…} vs do … end
->() {…}, -> {}, lambda {|| }, lambda {}
def mtd(a, b), def mtd a, b
0..10, 0…10
%w{}, %w[], %w(), %w//, %w``, %w"", %w||
%w[], %W[], %i[], %i[]
//, %r//, %s[]
'', ""
Slide 17
Slide 17 text
&& versus and
n = 1 && n + 1
# NoMethodError: undefined
# method `+' for nil
n = 1 #=> 1
n = 2 && n + 1 #=> 2
n = 2 && n + 1 #=> 3
n = 2 && n + 1 #=> 4
n = 2 && n + 1 #=> 5
n #=> 5
n = 1 and n + 1 #=> 2
n = 1 #=> 1
n = 2 and n + 1 #=> 3
n = 2 and n + 1 #=> 3
n = 2 and n + 1 #=> 3
n = 2 and n + 1 #=> 3
n #=> 2
Slide 18
Slide 18 text
|| versus or
n = 1
n = nil || n + 2 #=> 3
n = 1
n = nil or n + 2
# NoMethodError: undefined
# method `+' for nil
Slide 19
Slide 19 text
Values against expressions
You should know!
Slide 20
Slide 20 text
Boolean Algebra Fail
If && and || are boolean
operators, why their in/out
arguments aren’t booleans?
true && "string"
#=> "string"
1 || nil #=> 1
Slide 21
Slide 21 text
Ruby is not that strongly-typed language.
Slide 22
Slide 22 text
nil
I call it my billion-dollar mistake.
– Tony Hoare, inventor of ALGOL
nil is
• more code
• checks
• special cases
• error handling
• more specs / tests
• more errors
Slide 23
Slide 23 text
Rails’es Bad Parts or why Ruby сan’t “Choo-choo!”
Slide 24
Slide 24 text
Polymorphic
associations
At first glance polymorphic
associations look pretty well, but
their usage result in a lot of
problems when business logic is
growing.
Good schema
• Article has one ArticleCommentable
• ArticleCommentable belongs to Commentable
• Commentable has many Comments
• Comment belongs to Commentable
Slide 27
Slide 27 text
Leak of abstraction
ActiveRecord forces us to use
implementation details in business
logic (associations, queries, joins,
etc.) while what we need is to
separate them from each other.
Slide 28
Slide 28 text
Solution: DDD
Use Aggregate pattern to define
associations and dependencies.
Use Aggregate’s Root Entity to
request associated entities.
Slide 29
Slide 29 text
Single Table
Inheritance
STI is the way to make troubles
instead of relations (tables) and
the superclass makes no sense
in most cases.
Slide 30
Slide 30 text
Subclasse’s logic differences from its superclass, so
you can’t use superclass to get a superset of all
records in many cases.
class Article < Publication
has_many :comments
# You can’t do this:
Publication.order(published_at: :desc)
.includes(:comments)
.where(comments: {approved: true})
Slide 31
Slide 31 text
More problems resulted by STI
• We can’t use DB-layer validation and constraints (uniqueness
and NOT NULL, for example).
• We need an additional “type” column for metadata, but AR
models don’t protect it as a metadata-column.
• Totally misused cases of STI, like states, statuses, roles
(something changeable).
class ProcessedOrder < Order
end
class Admin < User
end
Slide 32
Slide 32 text
Good scheme
• Article shouldn’t be inherited from Publication, but Article
(Aggregate) is a composition over Publication and other
entities.
• ProcessedOrder shouldn’t be inherited from Order, but
Order (Aggregate) is a composition over OrderItems,
Delivery, Payment, etc.
• Admin shouldn’t be inherited from User, but Admin
(Aggregate) is a composition over User and other related
entities.
Slide 33
Slide 33 text
Compositional approach is awesome!
Slide 34
Slide 34 text
ActiveRecord is a
Golden Hammer
ActiveRecord as a pattern
became a golden hammer. It is
good for some limited purposes,
but we should not use it in every
case.
Slide 35
Slide 35 text
ActiveRecord is good for mapping a tuple to an object,
but bad for everything else. The most bestest alts:
• Vanilla SQL and PORO
• DataMapper
Slide 36
Slide 36 text
ActiveRecord’s poor
architecture
What AR models are responsible
for?
• Querying
• Persistency
• Associations
• Validation
• Business logic
• Triggering
Isn’t that too much?
Slide 37
Slide 37 text
Unit tests are a dream
Rails developers don’t write unit-
tests for models, because model
has a lot of responsibilities.
Here mock. There stub. There
one more mock. Mock, mock,
mock, … Stub. validate :title, presence: true
has_many :comments, dependent
def add_comment(attrs)
comments.create attrs
end
before :create, :assign_slug
Slide 38
Slide 38 text
Pseudo (surrogate)
keys
ActiveRecord and Rails force you
to use pseudo-keys instead of
natural keys. In most cases you
have no ID or anything of the
kind. Your records should be
identified with a natural key even
if it could be composite.
Slide 39
Slide 39 text
Follow me
• Home page
http://www.volodymyr-melnyk.com
• IT Blog
http://www.itrampage.com
• Me on Facebook
https://www.facebook.com/egotraumatic