This presentation will talk about the challenges of building large Ruby web applications and how to maintain existing ones.
I will use examples adapted from real applications that I worked on during my 10 years of experience with Ruby outlining: technical limitations of the language, how to use a modular dependency structure to enforce boundaries in complex domains.
21. 6 months later
– The law of continuing change (1974) Lehman, M
“Any software system used in the real-world must change or
become less and less useful in that environment.”
– The law of increasing complexity (1974) Lehman, M
“As a program evolves, it becomes more complex, and extra
resources are needed to preserve and simplify its structure.”
29. # lib/blog/after_publish.rb
module Blog
class AfterPublish
private
def subscribe_blogger_to_promotion
Promotions::Submission.new
end
end
end
# lib/promotions/new_member.rb
module Promotions
class Submission
private
def fetch_member(id)
# lib/membership/finder.rb
Membership::Finder.new(id)
end
end
end
promotionsblog membership
namespaces
context context
45. piadina gem
pizza gem
C
shared
ingredients
gem
B
main Ruby application
desserts gem
D
A
E
calzone gem
pizza dough gem
F
Conway’s Law
“organizations which design systems … are constrained to produce designs which
are copies of the communication structures of these organizations"
48. A
C
D
B
E
your health plan
drug information
claims platform
product
information
membership
gem
gem
gem
gem
gem
dependency
main Ruby application
Sinatra / Rails / Hanami
50. !"" Gemfile
!"" Gemfile.lock
!"" local_gems
!"" run.rb
#"" spec
path 'local_gems' do
gem 'health_plan'
end
source 'https://rubygems.org'
group :test do
gem 'rspec'
end
bundler’s Gemfile uses
a path directive to find
local gems
main Ruby application
51. !"" Gemfile
!"" Gemfile.lock
!"" local_gems
!"" run.rb
#"" spec
bundler’s Gemfile uses
a path directive to find
local gems
path 'local_gems' do
gem 'health_plan'
end
source 'https://rubygems.org'
group :test do
gem 'rspec'
end
main Ruby application
http://teotti.com/gemfiles-hierarchy-in-ruby-on-rails-component-based-architecture/
52. !"" Gemfile
!"" Gemfile.lock
!"" local_gems
!"" run.rb
#"" spec
directory where your local gems are
$ cd local_gems
$ bundle gem health_plan
create health_plan/Gemfile
create health_plan/Rakefile
create health_plan/LICENSE.txt
create health_plan/README.md
create health_plan/.gitignore
create health_plan/health_plan.gemspec
create health_plan/lib/health_plan.rb
create health_plan/lib/health_plan/version.rb
Initializing git repo in /Users/me/code/lab/gem-dependency-structure/local_gems/health_plan
$ rm -Rf health_plan/.git*
bundle gem can create gems
main Ruby application
55. # local_gems/health_plan/spec/health_plan/aggregator_spec.rb
require 'spec_helper'
describe HealthPlan::Aggregator do
describe "#details" do
it "should not throw exceptions" do
aggregator = HealthPlan::Aggregator.new(12345)
expect(aggregator.details).to eq({ name: 'The full package plan'})
end
end
end
your health plan
!"" Gemfile
!"" Gemfile.lock
!"" local_gems
$ #"" health_plan
!"" run.rb
#"" spec
56. # local_gems/health_plan/lib/health_plan/aggregator.rb
module HealthPlan
class Aggregator
def initialize(id)
@subscriber_id = id
end
def details
{ name: 'The full package plan'}
end
end
end
# local_gems/health_plan/lib/health_plan.rb
require "health_plan/version"
require "health_plan/aggregator"
module HealthPlan
end
gem entry point
your health plan
!"" Gemfile
!"" Gemfile.lock
!"" local_gems
$ #"" health_plan
!"" run.rb
#"" spec
57. !"" Gemfile
!"" Gemfile.lock
!"" local_gems
$ !"" drug_information
$ #"" health_plan
!"" run.rb
#"" spec
your health plan
drug information
main Ruby application
$ cd local_gems
$ bundle gem drug_information
create drug_information/Gemfile
create drug_information/Rakefile
create drug_information/LICENSE.txt
create drug_information/README.md
create drug_information/.gitignore
create drug_information/drug_information.gemspec
create drug_information/lib/drug_information.rb
create drug_information/lib/drug_information/version.rb
drug information
58. # local_gems/health_plan/spec/health_plan/aggregator_spec.rb
require 'spec_helper'
describe HealthPlan::Aggregator do
describe "#details" do
let(:fetched_drugs) { 'something' }
before do
fetcher_double = double('DrugInformation::Fetcher', details: fetched_drugs)
allow(DrugInformation::Fetcher).to receive(:new).and_return(fetcher_double)
end
it "should not throw exceptions" do
aggregator = HealthPlan::Aggregator.new(12345)
expect(aggregator.details).to eq({ name: 'The full package plan’,
drugs: fetched_drugs })
end
end
end
your health plan
!"" Gemfile
!"" Gemfile.lock
!"" local_gems
$ !"" drug_information
$ #"" health_plan
!"" run.rb
#"" spec
66. A
C
main Ruby application
B
loaded in memory, deamon or webserver
unit tested
unit tested
not unit tested
http://teotti.com/create-dependency-structures-with-local-ruby-gems#gotcha-flaky-bugs-caused-by-missing-requirement-statements
69. A
C
D
B
E
main Ruby application
F
H
I L
modular monolith!
membership
payment API
payment
platform
bank
transaction
credit card
transaction
your health plan
API
drug information
claims platform
product
information
persistence DDB
82. A
C
D
B
E
main Ruby application
F
H
I L
membership
payment API
payment
platform
bank
transaction
credit card
transaction
your health plan
API
drug information
claims platform
product
information
persistence DDB
83. main Ruby application
your health
plan API
drug
information
claims
platform
product
information
persistence
membership
payment
API
payment
platform
bank
transaction
credit card
transaction
DB
84. deploy parts of a monolith
http://teotti.com/deploy-parts-of-a-ruby-on-rails-application/
101. gems require a bit of experience,
gems require you to work a bit hard,
this person doesn’t write maintainable code yet,
that person doesn’t understand ruby gems yet
gems require talented developers
gems are only for smart developers,
this person always write unmaintainable code,
that person will never understand ruby gems
https://www.youtube.com/watch?v=W47rcJowx7k
http://www.amazon.com/Mindset-The-New-Psychology-Success/dp/0345472322