Brandon:Oh, hi.
A hush falls over the room
because I'm not paying attention.
My slides came up. Hi.
Really quick first,
there are seats in the middle
if people can squish together,
otherwise people are
going to be sitting in the
aisles and the fire marshal
is going to get angry.
I don't know that, I'm not a building guy.
Hi everybody, sorry to
make you move around
but also maybe that will
wake you up a little bit.
This is going to be a shot of espresso,
we've got to go, go, go, go.
I have a very serious talk
about the heart of architecture.
As you know, as DHH said at the
first talk, we are engineers.
What we do is build bridges with code.
We are architects, we build
centuries old castles.
We are craftspeople, we are
old-timey metal workers.
And we live in the cloud.
I think Javascript is ready
for all of these things.
I feel like Javascript is
ready for Cloudgineering.
If anybody knows what any of the things
I said with the pretty pictures are,
please grab me after the
talk and explain them to me.
I just say them to sound smart and serious
about programming, it is serious business.
Hi I'm Brandon Hayes,
I work with Charles
actually at The Frontside.
I'm a dedicated Cloudgineer
and no one ever asks which of
us is the smart one when
we converse together,
which is a little weird.
These are my credentials.
As you can see I'm highly credentialed and
ready to tell you all about programming.
My wife gave me a few of these,
most of them are actually true.
I'm not here to talk about some
core ideal or some big fancy thing.
I live in the real world.
I have to write programs that
people want written fast and
want them yesterday and it's
very challenging some times.
I understand that
architecture is great but
I don't have a lot of use
for ivory tower people,
I think the universe has
plenty of uncles in it.
We live in a place,
and I'm going to tell you a
tale about a couple of places.
It's actually a meta talk about the
ball of mud architecture pattern,
that's one I can really get behind.
If you're not familiar
with this, Brian Foote,
I believe in the late 1970s,
described an architecture
pattern that everyone here has
seen called the ball of mud.
To do that I need to
talk about city planning.
Most cities are not designed for growth.
Salt Lake City would be a rare exception.
You'll see it handled its
eventual growth pretty well.
From layout to infrastructure
it seems to have avoided
many of the problems
that plague large cities.
Our code does not live in Salt Lake City,
they don't work like that.
My current home town of
Austin is a lot more typical.
I look at that every day
on my drive home while
Charles rides his bike
home, which is great.
I get to sit in that every day for
about 45 minutes on the way home.
The city layout and
infrastructure was not prepared
for the kind of growth
that they've experienced.
That results in the dreaded urban sprawl.
But we don't actually even live here.
Let's be honest about our
code for a minute people.
We live here.
It's not a sprawling metroplex
or a beautifully planned city.
We live in a favela.
A favela is a Brazilian slum, basically.
It looks like cool though, right?
It's the definition of a
walkable city -- well, hikeable.
Let's zoom in.
A favela is a Brazilian
shanty town made permanent.
The structures require little
skill to create and they
pop up everywhere out of
whatever materials can be found.
The problem is these structures are
difficult to grow, to maintain or protect.
There's little police
presence or fire presence,
and safety and crime
issues are pretty rampant.
Forget architecture stuff,
we're going to go in to a code favela,
it will look familiar to you.
You've probably had some that made you
nearly go crazy trying to maintain it.
I apologize in advance
for making you look at
this but I really need
you to feel my pain.
That favela is a
manifestation of the ball of
mud pattern, which is
really easy to create.
You just need to build
something temporary,
add to it, and then rely
on it for your business.
They're so easy to create
that it's the dominant
architecture pattern, I would
contest, in software today.
Let's talk about how this happens.
Should be pretty easy right,
you can probably make
this happen this week.
I need a show of hands.
How many of you hate being
asked to raise your hands in a talk?
I should see no hands, that is insane.
How many of the rest of you have a
prototype that is shipped to production?
Pretty good balance there.
They say, "This is a two week feature,
"let's ship this thing in two weeks."
I don't believe in two week features,
I don't think that's actually a thing.
We do try to cram features
in to two weeks and then make
sacrifices to get them out the
door, or we inherit code from
less experienced developers
and it has to ship.
But mostly these shanty towns
start as a quick prototype.
Let's go ahead and build a shanty town.
It starts off like pretty much any other.
Let's say we're all working on Giffindor.
It's a social network for animated gifs.
The founder is a huge Harry
Potter fan, cosplayer,
the whole thing. Has the
glasses, it's adorable.
Personally I'm more a Hufflepuff,
this is not my website though.
It's a vanilla server-side app in Rails.
One note on pronunciation.
I'm going to stake a stand.
I'm going to say that gif is
pronounced like GitHub Gist.
I do the same thing
with JSON and [JAY-sin].
I like to make everybody mad.
Mix tabs and spaces, people love me.
Back to work.
Let's just sprinkle in
a little interactivity.
Let's start with sprinkles, they're fun,
sprinkles are fun, yay!
Your boss calls. She says,
"We want a better experience
"on the site. Our users
demand it. They should not
"have to go through a
page refresh all the way
"to the new page to submit
a new animated gif."
You look at your Javascript file.
Nothing there, undaunted
you march forward.
Easy right? Just make a little HTML form
on the page with show hide, all done.
That worked, except it submits and
does a full page refresh on submit.
"Can you do that by AJAX,
"it's kind of irritating to our users?"
All right, that's what AJAX is for, right?
We submit this thing via AJAX.
That seems to work, all right.
But now you actually
have to add that new POST
to the list of POSTs
that are on that page.
All right, no problem.
Okay, a little bit of a problem.
We're starting to duplicate
some DOM code here in your
Javascript so you get to
edit stuff in two places.
A little duplication. We know,
we don't de-duplicate first.
You're agile so you agile that code right
in there until this whole thing is agile!
(laughter and applause)
By the way that's a trademark
of Cloudgineering Inc.
so do not use that without
written permission.
So this sprinkle is starting
to turn in to a little more
of a rain shower, a little
bit of a steady rain.
But cheer up Keanu, we
shipped some software, right?
Okay, sport. The product
manager, who tends to look
like Fred MacMurray for some
reason, is really happy.
He has more ideas. He wants
users to be able to click
a cancel button so that
they zero out the form,
because if you open it
it's just stuck there.
Dutifully you implement the cancel button
and zero out the form with jQuery.
Awesome, except now users
are hitting submit on
empty and invalid forms,
so we need some sort of
client-side validation to
prevent that from happening.
It shouldn't be too hard to tell if
somebody has attached
an animated gif link.
It wasn't super, super
easy but it was relatively
straight forward. We disable
the submit button unless
it's valid and show messages with jQuery.
This is starting to look
a little weird, right?
It's starting to really come down.
Poor Al Roker.
Your CEO comes in to congratulate you
on all the great work you've done.
You've really shipped a lot
of code and agiled everything
and you just need a
couple more enhancements.
Let's add an inline preview
for that image so users know
what they're about to
post before they post it,
and a little character
count that doesn't ding them
for long urls. We want
a smart character count.
That's working but
actually broke a couple of
other things about the state of this form.
We do some double checking
and enable or disable submit.
Now you're caught
between these two worlds.
You want to craft code that
you can be proud of and that
lets you feel good about
the things that you do,
but you also want to
be able to ship stuff.
Your business needs you to
move fast and break things.
You need to get stuff done,
GSD. Craftsmanship, ship it.
I say craftmanship it.
(laughter)
(hesitant applause)
It's a good thing you're
a cloudgineer and you
can straddle these two
worlds just perfectly.
Look at all this code that
we've craftsmanshipped.
Sandy Metz talked
yesterday about code shape
and the squint test as an indicator.
When I squint this code has a shape and
that shape is a sack of hot garbage.
(laughter)
I don't know if Chicago
has this but in Manhattan
in the summer time you get this nice smell
wafting off of the hot
garbage, it's great.
That's kind of how it feels.
Here we are, a total tsunami
of entangled jQuery code.
(laughter)
Let's ship a feature against that.
Please, it's really important!
Your CEO is now putty in your hands,
you are a superstar
developer and the expectation
is now that you just shipped so fast.
And a light button, let's do it.
Here's the thing. I was going
to implement this in jQuery,
I was going to do it
but there was already so
much double checking and
interwoven states that
I physically couldn't
without getting really ill.
The thought of touching
this code made me want
to quit writing the talk and just be like,
"You know what RailsConf, thanks anyway."
Now what, what do we do?
Actually Brian Foote has prescriptions for
dealing with the ball of mud pattern,
and it's not always what you think.
You don't necessarily dive
right into a refactor.
Addressing a ball of
mud is pretty difficult.
We can sweep it all under the rug,
or put it inside of a black box.
You can reconstruct it,
tear the whole thing down
and raze it and rebuild it.
You can renovate block-by-block,
which he calls keeping it working,
or you can quit you job,
which in some cases,
sometimes it's the thing to do.
Black box is fine if you know you're never
going to have to touch that code again.
If it's some complicated math equation or
something I've seen that done, where you
just hide it in the
closet and that's fine.
A rewrite is a great way
to learn really amazingly
hidden lessons about your business logic.
Things that are encoded
in very strange places.
It's a really great way
to make something that
sounds like it takes two
weeks take six months.
I feel like you always
discover that hidden
business logic so let's
talk about a refactor.
Why would we do that?
We decided that refactor
is probably the right
thing to do because the feature has to be
maintained but it's now
too expensive to manage.
Users are starting to
actually have a bad time with
it because the longer
they stay on the page the
more likely they are to
have problems with it.
And more importantly to me personally is
that you are tossing
and turning all night.
You cannot sleep because
you're dreaming about the JIRA
tickets that are waiting
for you in the morning.
Why are we still using
JIRA, why is that a thing?!
Okay, anyway, separate deal.
You think about your
frustrated users, your gut is
telling you to fix it and
your gut is totally right.
You have two paths out of here.
I think the person in this
room, super smart guy, is like,
"Why don't you refactor this
to smart Javascript objects?"
He's largely right. That's an
ideomatic way to dig out of this.
He also asked me if I
did that as a strawman
of how to build really
terrible jQuery code.
I was like, "Yeees. I
was not doing my best."
The other option is to use a framework
to abstract away the DOM and handle data.
Our goal is to get the heck out of
the DOM as fast as humanly possible.
That's our biggest pain point right now.
For me I'll choose a framework.
The framework will manage the DOM and
we'll just manage the underlying data.
All we need to do now is
five steps to get out.
These steps will probably
apply for people that
use plain Javascript and
don't use a framework,
or any framework that you choose.
It's just nice to build up against one,
personally I certainly prefer it.
On our blog we'll talk more about why
we choose Ember and why we like it,
and you heard Charles mention
earlier if you were here,
about why we like Ember's model layer.
I feel like it's a really
strong model layer for
the case that we're
trying to accomplish now.
It has really great
bindings and managed state,
and it's got a great drop in component
library to tie it all together.
Step one, let's rap it. Rap
it, rap it, rap it, wrap it.
A little sidetrack, a lot
of people don't realize
that you can sprinkle Ember into an app.
A lot of people think that
Ember only works if you want to
start an app from scratch and
build up from the ground up,
from floor zero all the way to
the top, but you can actually
apply it to a lot of
codebases and it's really
great for refactoring
existing codebases toward it.
That's just these three steps right here.
At The Frontside we do
that pretty often where
we take an existing Rails
application and move
it towards Ember, but it's
actually not that hard.
First things first it's
time for some justice.
We're going to put that
terrible code in code jail.
That code jail is this
initLegacyCode function.
We're going to create an Ember
component, we're going to
put the stuff in initLegacyCode,
which is not a special name,
I just chose it to tell
me that this is the jail.
It bootstraps the old
code inside the component.
We do not alter that code at all.
Also we move the HTML in to a handlebars
template associated with this component.
No structure of that
HTML actually changes,
we're just isolating.
Then we can sprinkle. Now we
use jQuery to stuff all of
this old stuff that we had
before back in to the DOM.
Another thing Sandy said
yesterday that I hope
it's self evident, it's certainly
made itself self evident
to me over the course of the
last year or two doing this,
that you have to test it.
If you do not test your code a refactor is
essentially impossible, I
cannot imagine trying to do it.
I was not a big testing advocate earlier.
I was very skeptical,
let's say, about testing.
I was raised by cowboy coders in my first
place when I was learning to program.
But we're going to do it and
we're going to see that it's
actually not that hard to
actually test the thing itself.
We're going to test that
it shows up and it passes.
I'm doing a little bit of a hand wave
here over Javascript testing setup.
Javascript test setup story
for getting Ember applications
bootstrapped in Rails can
be a little challenging.
There's a growing body
of knowledge about it.
I want to give you guys
a quick note on that.
On its own, Javascript
testing is pretty easy
but when you add in frameworks
and tie it to Rails,
add in the asset pipeline,
things can get a little complicated.
It's a little bit of a wild west,
there's a lot of choices out there.
There's a little bit of
paralysis of choice I think.
It's really wide open,
but not like home on the
range wide open, more like
this kind of wild west.
It can lead to that, you
look at the thing of,
"I don't know what brand
of toothpaste I want,
"I just want some toothpaste."
There's not really a truly accepted happy
path just yet but it is getting better.
Things are maturing,
we're building schools,
we're building hospitals still.
There's a lot of a need
for libraries and blog
posts and people finding
these happy paths.
Taking aside for 15 minutes,
there are screen casts
being released right now,
there's a thing called
Ember Sparks where he's
teaching people how to
set up and bootstrap your
application environment.
We're all still figuring this
out there, so be prepared
for a little bit of rough
take off on this still.
Another thing is testing
AJAX can feel a little
intimidating when you're
trying to test Javascript
but it's actually pretty simple.
There are multiple libraries
to do this kind of thing
I like ic-ajax.
It intercepts AJAX calls and
allows you to inject fixtures in.
Here we're going to test each
path through the experience.
In this one we're testing that clicking
submit shows a success message.
We just want to go through
and verify that all the
things that the code says
it does it's actually doing.
We're not changing any code,
we're just wrapping it in test existing.
And that passes.
Okay. At this point we
repeat for each path
to create an integration
test for the entire
application stubbed at the API level.
This took several hours
but it's really important.
As I said if you don't have a test harness
I do not know how you can refactor.
Maybe if somebody has another
answer we can discuss it,
because testing is not my
favorite thing in the world.
We can now move forward
with some confidence.
Go ahead and take yourself a
victory lap, you earned it.
This is, however, just the start.
Step three is to identify models.
You have this blob of code,
how do you find models in here?
Your server MVC might give you some hints.
I don't believe that your
models will map one to one,
but it's a really great place to start.
In this one we actually get to
start driving with the tests.
Sometimes I test drive, sometimes I don't,
I'm not an expert at either
style, but I do think that
in this case it was pretty
good to say, "I know that I
"have a model and I know
part of what a model is
"going to parse out an animated gif url
"and tell me whether it
is one or is not one."
With no code that thing
should fail, and it does.
Now let's extract it.
We can actually extract some
of that logic to a model.
And we can extract the
animated gif link from
the POST body and give it a property here.
This is called a computed
property where you see parsedUrl.
It does some functiony stuff
and then it has a little
declaration at the end
that says property body.
It means that we're going
to depend on the body,
which is the blob of data
that somebody just types in,
the text that they type in.
At any time a person changes that,
any time that changes
it's going to observe and
update this parsedUrl
property on this object.
Now those unit tests will pass
and we can start turning that
static content into the dynamic
content using handlebars.
This lets us kill some
code, that's pretty awesome.
This should get the older
acceptance test passing again.
It is the coolest feeling in
the world to write some tests,
take some code, scrub
some garbage out of it,
do something really dangerous feeling,
like jump off the cliff
and know, when you rerun
those tests and they start passing again,
and you start getting green
again after a refactor,
it's really fun.
In this case we didn't do that much,
we didn't have to write
that much code because
we let the framework
carry our matched luggage
for it which we cannot live without.
The models hold onto your data,
they keep it up to date and
the changes in the DOM are
just going to automatically
propagate out of that.
Now we're going to lean even
harder into the framework.
It actually is sort of the
fun part, identifying the
states is more fun than
identifying models, I think,
because we get to go back through the
app and pick out what these states are.
In this little widget the first state is
a blank state, a initial state where the
button is just visible
and nothing else is.
When we click it the button goes away and
the thing is in a ready-to-save state.
While data is in flight
we're going to disable
the post button, leave
everything else the same.
On an error state we're going to leave
the text intact but display a message.
On success we're going to display a
success message and hide the form.
After five seconds we want it
to reset to that initial state.
That's kind of the business
logic that we wanted
in the first place but we
incrementally built it.
That's an okay way to
find out what you want but
not a great way to ship to
production, as we found.
The component starts
in this initial state.
We bind that state to a
class on the component's DOM,
and then we'll use that later to let CSS
manage what is shown and what is hidden.
We want the DOM to just
be a representation of the
state of the app. The DOM
is just there to represent
the app's state at any given time.
It's kind of almost read-only.
Now instead of managing
the DOM with jQuery
we're going to have buttons fire off
actions that just push the state around.
"You go here, you go here,"
they're like the train conductors
in a Japanese train station.
It's kind of a lazy developer's state
machine but it's going to do for now.
Step four, that's done.
That was all the code we needed for state.
Step four is to break up the
remaining code left in code jail.
The states and model are
in place and are tested,
but there's a couple of ugly things left.
Let's look at it. Code jail is not empty.
The legacy code still reaches outside the
component to delete things in a wonky way.
And, heh heh, hi, how are you.
I want to rub your nose in this code,
even though I'm the one that wrote it.
What could possibly go wrong here, right?
This POST is pretty scary.
It's painful to modify,
it invites pain for the
user but listing POST
is outside the responsibility
of this component
we've created so what can we do about it?
Actually we have a pattern
that we've already laid out.
Let's create another component and
sprinkle it in for listing those POSTs.
Another quick detour in to Ember Data.
At this point I have a choice to make.
Do I want to make sharing
this information with you more
complicated by talking about
Ember Data and I think I do,
it actually made the
next steps much easier,
and this detour actually
took me about 20 minutes to
replace the model with
an Ember Data model.
Ember data is basically a
wrapper that talks to AJAX
for you and converts to
an Ember model for you.
It buys us lots of good things.
Basically took just a few minutes to do.
That's it, we just declare
it as a model on Ember Data.
I like to wire in an ic-ajax,
just like we did before,
to Ember Data and this is all
the work that it takes to do.
Fixtures will still work as written.
This was all it took to get
the tests passing again.
That's pretty much it
for the list component.
As you can see I have a delete function,
a place to reference the list, that's it.
It has a list of those posts
and a way to delete one of them.
All I'm doing is
destroying a model record.
The second set of tests here is pretty
much going to follow the
first. This test shows
that the animated gifs
show up as expected,
and from here we can drive
out the delete functionality.
We have a second template to
replace the server-side markup
and then we sprinkle it in
just like the first one.
We say, "Hey, jQuery, take this
component and stuff it in to
"the DOM for me where that
list of posts used to be,"
and Ember is going to
show that for us now.
That's interesting, we
have two components.
How are they talking to each other?
Well they both react to
changes to the underlying data.
There's a pool underneath that is the
same shared data set but the components
are completely isolated from one another.
They can only communicate by altering
that pool of underlying data.
Changes in one place can affect the other.
There's a lot of boilerplate
code we did not write here.
We just trust that the
data layer is going to do
its job and the DOM will
represent it properly.
One thing that happened though is because
we're not doing stuff
with jQuery UI animation
stuff any more we lost
show hide functionality,
so let's put that back.
We're going to use CSS transitions
which buys a lot of benefits,
including hardware acceleration
on more modern browsers.
Remember how we bound class
name to form state earlier?
We said there was a thing
there that says form
state is bound to the class
name for this component.
Now we get these classes
for free automatically,
Ember is going to stuff that in to our
component every time the state changes.
It pushes the state, it
changes the name of the class.
I like to start by creating placeholders
for the various states
in a given component,
and then put different
behaviors under those states.
In this you can see it
almost tells a story.
We start an initial state with a height of
zero px and an opacity of zero.
Then once it's in another
state it actually expands.
Everything except initial it
will transition to that state.
We transition open to visible
with a 200 millisecond delay and a ...
You know what, I'm just
going to show it to you.
It's not great to try to
explain how CSS animations work.
Let's do that.
I think I need to make
that window smaller.
That's great. Thanks Obama.
We're going to submit and now it expands.
Here we're in a validation error state.
We have the gif preview.
We have zero characters
because that smart preview is
telling us that we're not
using up any characters.
And we'll post it.
Oh, I just got trolled by chrome caching.
This one is just great.
Julie Andrews is so awesome.
I have many of these that
I really love very much.
Instead of doing that all day maybe
we should go back to the talk.
What we're not doing is we're not
manipulating the DOM directly.
If we want our tests can
actually relax about the DOM
a little bit, we don't need to
necessarily test the framework.
What we can focus on is application logic.
We can still integration test if we want
but let's look back and
see what we've done so far.
There's some old code right here.
Let's see if you can locate
the business logic in here.
Right? You can't.
That's why I'm introducing a powerful
encryption algorithm called jQCS, 512-bit.
It scatters your business logic across
hundreds of lines of jQuery code.
You can stop worrying
about whether hackers
are going to access your
business logic in your
front end because even
your programmers can't.
(laughter)
That's cloudgineering folks.
Also a trademark.
That turns out not to be
such a super hot idea.
We've now refactored
this into a place where
we've moved that encrypted
logic and exposed
it via computed properties,
states and models.
The business logic is
actually front and center now.
We've got from this
sack of hot garbage to a
tested and documented
reliable implementation.
It's not perfect but now it
begs to be extended and reused.
From here what happens
next is really up to you.
From here, most of the rendering that's
happening on the page
is being done by Ember.
If you want to replace it
and get all the benefits of
having a router underneath
your app, great, go nuts.
You can actually now take
application handlebars and
have it render those to components
for you, and you're maybe
a few hours from having a
full single page application.
Or you can continue to
sprinkle in more components
and let your Rails app do
all the driving and let your
components do the more dynamic
pieces of your application
for you with that pool of
shared data underneath it.
Now my brain is tired.
Why did we do all this
stuff, what even is my life.
We had one job to do, a job
that we were just about to quit,
but right before you became
millionaires in the IPO.
I found out a couple years ago I quit
a job that just recently IPO'd.
I would be on some yacht.
But we didn't quit,
because we love our users,
we love our coworkers, and
we want to love our job.
Software development, it really is fun.
It can be frustrating but it
shouldn't give you night sweats.
Let's try this one more time against
our new and improved code base.
Once we know the API
it's a small test to just
verify that this thing does what we want.
We can add a little
favorite or like feature.
We stub out the data, test that
a favorite makes it favorite
and an unfavorite makes it unfavorite.
Build a little favorite
model that talks to
the server and it has
many relationship here.
You can see DS belongs to gifPost
and it has many on the other side.
And add some dynamic content
to the handlebars template.
Then we write a toggle action to create or
destroy the favorite and
that's pretty much it.
If chrome caching will smile upon me
We'll demo that as well, I'm
going to switch my branch.
Everybody knows this trick, right?
Also everybody knows the
trick of typing rails s.
It's a little known thing,
most people don't know.
It's how you load a Rails server up.
(laughter)
You can see my responsive
design is so badass.
Woah, star just flies all around.
Now we can favorite this thing.
It's actually talking to the
server on the back end but
it was so easy to do this
that we actually were like,
Well, do you want me to filter
these favorites for you?
I can do that and just
show you my favorite ones.
This one, this is legendary.
(laughter)
(laughter)
(laughter)
Now we have favorites.
If I unfavorite something
it will automatically pop out of the list.
I'm not managing any of
this stuff. I just built a
little tiny filter that took
like five minutes to do.
This stuff buys you so much and extending
this actually becomes very fun.
We've changed our relationship
now to this code base,
where we actually like working on it.
We wake up in the morning
instead of feeling
sick about what's going
to be in our inbox we're
actually really excited
about what we get to do next.
I just want to tell you as
an addendum to this that my
personal story about, my
first conference was about
four years ago and I think
a lot of people, can I get a
show of hands of who's hear
that's their first conference?
That's a really great portion of the room.
My first conference,
I went to a hack night the
night after the conference.
It was put on by some really great people
but I walked in, I looked around
and I didn't know anybody.
I listened to some people talk.
I was just learning to program.
I was listening to people
talk and they were so smart
and they were people that
were so well known in the
community and I was so
nobody that I immediately
turned around, left, ran
out of the room as fast
as I could, got in my car
and cried and drove home.
Which is pretty awesome,
I'm like super cool guy.
(laughter)
After that point the
community picked me up and
dusted me off and said,
"Get back in there kiddo."
Many people in that community
that I didn't know in the
room at the time became
some of my closest friends.
These people in this room, if
you'll look around, are some
of your future closest friends
and colleagues and mentors.
The community is the reason that
I get to do this for a living,
when before I was doing
just horrifying things.
If you can imagine what
it would be like to do
marketing for a multi-level
marketing corporation.
Just imagine the existential
hell and imagine programming.
We're pretty lucky to
get to do this and I want
everybody to have as good
a time writing software
as I get to have when I'm
doing stuff like this.
The other thing I want
to say, the last note
I want to say on community
is we have a tendency
to do this tent pole
thing where we raise some
people up and then gather
around those people.
That's not really what
a community is about,
a community is a mesh, it's
not like hub and spoke.
Every person in here has something to
contribute and something amazing to do
and I can't wait to go
to your talks later.
I'm told I'm pretty good at hugs ...
(applause)
I'm told I'm pretty good at hugs so come
say hi to me and thank
you so much. (applause)
(energetic big band music)