Thanks Mike.
Thanks for the invitation.
I want to start with a picture from this
building, which I have to say
I love this venue,
and especially this building.
And especially when it's not on fire.
And I think we were really dumb yesterday.
So if it starts beeping,
I will be leaving this time.
If you have not done it,
I suggest you walk up this round arc—
you can walk all the way up to the top—
and on that walk you will see that stairs
somewhere that says "The Beginning".
I want the beginning of my talk to be the
end of my last talk on a very similar topic.
And that was an idea that I was proposing
and that was the realization first of all
that every gem that you start,
the gem starts with a namespace.
When you develop a Rails app,
you never start with a namespace.
And so if you compare the two,
a gem is like a box.
You put a little label on it.
And a Rails app is a
gorgeous infinite nothingness
with all the bells and whistles of
Active everything.
To get a gauge of how relevant that is,
who writes Rails apps on a daily basis?
That's about two-thirds, maybe three-quarters.
And who writes Ruby,
but never touches Rails?
That's way less. Okay.
But still a few.
So I have a feeling that for the second
group, what I'm going to say is going to
apply a little bit less.
Maybe because of Rails not being around.
So the idea from my last talk was:
the next time you start an app,
just put everything in a namespace.
And I end it with what I found to be an
awesome finishing slide,
give yourself a box so you can start
thinking outside of it.
With the idea that if you have a gem,
and you give something a name,
and you put things inside of it,
you can open that box,
and you can go WTF when you find something
that obviously doesn't fit in the box.
If you try to do that with a Rails app,
you don't have a box—
you don't have a name—
so everything is going to fit.
The best game you can play is
what doesn't belong with the others.
Red. Green. Loud.
What doesn't belong with the others?
And you're going to know that it's Loud.
But if you look at your typical Rails app,
what are you going to have?
You're going to have a User, a Company,
Roles.
And then, the Tulip pricing engine that
you wrote.
And then the marketing part for that
and a store.
You're going to tell me that you can still
play that game and find out what may or
may not belong to this app?
It's very hard, because it kind of pushes
us in the direction of just putting all of
app in there.
So what I want to talk about today is
Component-based Ruby
and Rails Architectures.
I promise you I put this slide in before
that happened.
(Laughter)
I may want this button more desperately
than you. Trust me.
Stop me if I'm going too fast.
Because I want to talk about
large applications.
And one thing I heard about them is
you just never build them.
So I work for Pivotal Labs, and we
start a new project every few weeks,
and many of them are greenfield,
so many of them are tiny when we start.
But you heard from Matt yesterday,
we have pretty big apps as well.
And for every app, since our definition
of success is that a client can stay
successful after we leave,
what we have to strive for is some kind of
architecture where you can develop an
application well over time.
So we have to think about the app being
larger than it is, while we develop it.
Despite TDD and all.
Sandi Metz wrote her awesome book last
year and gave a few talks.
And at the one that I was at—
I don't know if she said it in other talks as well—
but she said, "Your app is out to kill you".
The growing complexity of your app is
going to chase you
and if the app were a bull,
you might feel like that guy.
And that is very exciting.
For the next second that is exciting.
For the next hours or days that is painful.
Okay, maybe this is a bit drastic,
but who has felt like their app was
behaving like that bull to them?
I have felt like that as well.
They are not typically that fast.
Quite the opposite.
But they behave like something
that is too big to handle.
So what I want to get to, is for us to
be more like this guy.
Calmer. In control.
With sunglasses and a hat.
(Laughter)
But I don't really like bull fighting,
so I actually want us to be this guy.
(Laughter)
Just hanging out.
I want to go home at five on a Friday night.
And I think this guy is probably the one
who is going to do that the most successfully
because he is just fine
probably all week long.
This "never build large apps" is part of a
larger quote from Justin Meyer:
"The secret to building large apps",
he says, "is never build large apps."
"Break your applications into small
pieces. Then assemble those testable,
bite-sized pieces into your big
application."
But how?
He was—I think—talking about JavaScript.
I'm talking about Ruby on Rails.
You can tell from the font that you're
supposed to read this slide.
(Laughter)
I'm going to use a tiny sample
application throughout this talk.
And this is where you can find it.
If you can't read this after all, just
search for github and the next big thing
and I think it comes up first.
I promise this thing is tiny
and it does only one thing.
It's an announcement page.
You can announce whatever you have to
announce.
And people can sign up for updates.
I spell it with two Ns but I don't know
if my Twitter handle is only one.
That's how often I tweet.
So the service thanks you for signing up
and I just press return.
And when I made this sample app
I had to come up with
a little bit more than just that
to actually have something to move around.
So I realized pressing return makes it
really easy for me to do that
many, many times.
But of course I only want my email address
to be registered once.
So I thought I'll give the server the
ability to kind of feedback the level
of annoyance that it feels while you're
just continuing to press return
and annoying it.
At some point it'll just freak out.
The freak out picture didn't come.
Let's try again.
It's an awesome picture of a guy with
a lot of hair.
I need your help though.
I need you to imagine something
really big.
Because that app is tiny
and I'm going to make examples
of refactorings or rearchitecturings that
don't make sense in that app.
So bear with me and always imagine
something very big.
I'm going to go through eight ways of
architecting this application.
And I have roughly two minutes for
every one.
Let's start with number one.
If you checkout that GitHub project,
you will find those eight steps as tags
and you can roughly follow the steps that
I'm taking.
The first one is a normal Rails application.
It's all-in-one.
Let's look at what's happening.
There is a TeaseController here.
It has two actions: new and create.
Let's briefly see what happens.
No surprises. It's not a well written
method. John would refactor this.
We're trying to find the entry that you
gave this form.
If we find it, we update tries on that
entry.
When we put it into this annoyance meter
it'll tell us how annoyed we are
at this point, and we'll feed that back
to the client.
If we don't find the entry,
we'll just say, "Thanks for signing up."
And then there's a bit of catching
unexpected cases.
That's pretty much what's going on in
this controller.
There is a model back in this.
It's Entry. It has the email address
and a number of tries.
And there's this AnnoyanceMeter,
which is not really important.
It just derives new strings
out of these counts.
I suspect that everyone has seen this.
And I will—for this talk—assure you
that it every one of these stages
you can run the tests, and—if I didn't
pick the wrong commit—they will pass.
So I have tests, but I won't show them.
I will just be moving them around for
every stage
and adding new ones where there is a new
test to be written.
Now remember, I said at the beginning of
every Rails app it's kind of this infinite void.
A good picture for that is probably
a big dump site.
I've already asked you if you ever felt
like the guy in front of the bull,
and maybe you also felt like the driver
in that bulldozer or whatever that is.
The reason I'm saying that about this tiny
app is that there's two things in there
that are now indistinguishable—if you look
at it from 30,000 feet—as to what they do,
how they're connected,
if they have any structure.
It pretty much doesn't exist.
So for all intents and purposes—
if you just multiply, extrapolate from these two
classes to, say fifty or a hundred—
you have no clue
who's interacting with whom,
what's going on, and why.
Unfortunately this doesn't
look very chaotic.
But if I were to write a hundred class
names here, it would be chaotic
and you would not be able to assess any
sort of structure
just by looking at those classes.
A first way of getting structure
into this application is modules.
The plus we get from that is
a higher level structure.
And you've all probably seen this one too.
Not much changes.
The controller doesn't change at all,
except it now references to Entry and
the AnnoyanceMeter within their
namespaces.
In the AnnoyanceMeter, Annoyance left the
class name; it's now in a module.
I moved some other stuff around
that was mainly for testing.
We now have this structure.
And if we come back to this analogy
then maybe we have moved
from a dump site to a recycling yard.
Recycling yards are awesome.
I should know this because I remodeled my
house, and you visit those places very
often when you do.
You can point to a corner of the recycling yard and say, "That's where the scrap metal is."
However if you've ever gone to such a place, you also know that you stick the hard-to-recycle stuff under your car because you're not a nice citizen
and then you try to pawn it off to the recycling yard because you just want to get rid of it and it's so hard to get rid of.
People do that all the time.
Just stand in front of the clean wood and then suddenly, not-so-clean wood gets thrown on top.
What does that mean for code?
It means there is structure.
I have now an EmailSignup module and an Annoyance module.
And in that is more classes.
But there is no guarantee that these classes are a) independent, or b) actually doing what they say.
I can't prove it.
That's why I put the rectangles over each other.
But at least I didn't have to write Entry anymore because I now have a higher-level concept, that being this EmailSignup module.
If I want to improve on this, I would like to go to the next step, which is prove that these two pieces are independent.
I call it the gem component app.
I may need to explain a little bit more about this step.
First off, you're not seeing that engines folder right there.
We're not looking at that.
We're looking at the annoyance gem.
I made a folder gems and I put an annoyance folder in there.
Actually this is a complete folder structure of a gem.
We have a gemspec; it defines our annoyance gem—and I should probably fill in this data—but there is a Gemfile which tells us that we are running tests with RSpec and magically—this may be the only time we need to look at tests—I have tests now in this gem.
If I go into that subfolder, I can now independently run.
Because of the way RSpec will load these files, I can prove to you that these tests for the levels and the meter of the annoyance will pass without knowing about EmailSignup.
Again, it doesn't matter here; it matters a whole lot if you're dealing with tens of hundreds of files.
I can prove that this thing is independent.
How many have seen shoving a gem like this into the subfolder of an app?
Everyone else should start doing that.
You could ask me two good questions: one is why am I not using git submodules? "I'm not smart enough" is the answer to that.
Why am I not using a different repository?
"I'm too lazy" is the answer to that.
You can just do this.
So how do I use it in the application?
The Gemfile of the main application is now just pointing to an annoyance gem and it's just referring to it by its path.
That's all there is to it.
This will be loaded.
I can access it like before.
And if I look into the TeaseController, we are still down here just loading that AnnoyanceMeter.
Nothing else special has happened.
The only thing that happened is we now have provably independent tests; we have provably independent code within that one component that we just extracted.
In the words of Eric Evans, "Choose modules that tell the story of the system and contain a cohesive set of concepts."
We are now able to prove that a set of concepts—namely within that gem—is independent of the others.
I think that is of great value.
I was in Iceland last weekend and I found something funny so I wanted to put these slides in.
It's good for push and pull to be so explicit, but if you look at the doors, it's actually not that explicit as to what they do.
They denote how to operate this door, but now look at Icelandic signs.
You know how to open these doors.
They scream at you, "This is how you open me."
You might also rip them apart, but you know.
(Laughter)
For the structure of the app, now there is a second component.
So we've got a little bit more structure.
Is that cool so far?
Gems make up a portion of apps, but if I have dependencies towards anything Rails, I have to do a lot of homework myself.
In the next step—what I want to call the Rails component app—I'm still going to do that for a Rails component.
So I want provable structure for Rails.
The way to do this easily is with Rails engines.
If a Rails engine were actually a train engine, then a Rails application would actually be a train.
So you might want to reconsider to call the whole thing Ruby in Trains.
But I still have to clarify a thing that engines have still this wrong perception of being for pagination and generic administration and authentication.
They are not.
If you look at the docs it says Rails engines allow you to wrap a specific Rails application or a subset of functionality and share it with other applications of within a larger packaged application.
I should know because I pushed that change last time I gave a talk like this.
It's very good that it says that now.
Let's look at what that means.
Now next to this gems folder we also have an engines folder.
Now you're allowed to notice that the app folder went away.
Since we only have one controller, there is no longer any necessity for this app to contain any code itself.
It only contains gems.
Let's start with the more self-contained one: EmailSignup.
If we go in here, there is still that Entry and it's now within a module and within a gem, which is also incidentally an engine.
If you don't know a lot about them check out this source code or find out about them.
I can't go into more detail but I'm happy to do that in Questions.
So essentially this means I have my database migrations in here; I have tests for this; it runs independently.
And the main app, as before, just includes those gems and references them.
Let's quickly look at the Teaser because the Teaser is now what contains the controller.
Actually I also want to point out that the Teaser contains assets, controllers and views in my example here.
It does not contain models because it doesn't have any data itself.
EmailSignup only has the model. It only has this entry thing.
The Teaser thus must be getting that data from somewhere else.
And indeed it does.
It too requires EmailSignup to be present.
And it references that dependency of course in the gemspec so that it's actually a valid gem definition.