00:00:05.500
Oh hi. Hush falls over the room because I am not paying attention. My slides came up high, so a quick note: There are seats kind of in the middle. If people can squish together, otherwise people are going to be sitting on the aisles, and the Fire Marshal is going to get angry. I don't know if I'm a building guy.
00:00:19.869
Hi everybody! I’m starting to make you move around, but maybe that will wake you up a little bit. This is going to be a shot of espresso; we got to go, go, go! I have a very serious talk about the heart of architecture. As you know, as DHH said in 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, and we live in the cloud. I think JavaScript is ready for all of these things.
00:00:46.690
If anybody knows what any of the things I said – with the pretty pictures and stuff – are, please grab me after the talk and explain them to me. I just said them to sound smart and serious about programming. It’s serious business.
00:01:03.370
Hi, I’m Brandon Hays. I work with Charles at The Frontside. I’m a dedicated cloud engineer. 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 am highly credentialed and ready to tell you all about programming. My wife gave me a few of these. Most of them are actually true, so I'm not here.
00:01:15.159
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 sometimes. 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. So, we live in a place, and I'm going to tell you a tale about a couple of places.
00:01:41.020
But it’s actually meta, talking 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. But to do that, I need to talk about city planning.
00:02:04.230
Most cities are not designed for growth; Salt Lake City would be a rare exception. You’ll see that it handled its eventual growth pretty well, from layout to infrastructure, avoiding many problems that plague large cities. But our code does not live in Salt Lake City. My current hometown 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 traffic every day for about 45 minutes on the way home. The city layout and infrastructure were not prepared for the growth they’ve experienced, resulting in the dreaded urban sprawl.
00:02:45.690
But we don’t actually even live here, let’s be honest about our code. For many people, we live in a favela. A favela is a Brazilian slum, and while it looks kind of cool, it’s the definition of a walkable city. So, let’s zoom in. A favela is a Brazilian shantytown made permanent. The structures require little skill to create and pop up everywhere just out of whatever materials can be found.
00:03:10.609
The problem is these structures are difficult to grow, maintain, or protect. There’s little police presence or fire presence, and safety and crime issues are pretty rampant. So, forget architecture stuff; we’re going to go into a code favela. It will look familiar to you; you’ve probably had some code that made you nearly go crazy trying to maintain it.
00:03:40.370
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. So, let’s talk about how this happens.
00:04:06.310
It should be pretty easy, right? You can probably make this happen this week. So I need a show of hands: how many of you hate being asked to raise your hands at a talk? Okay, I should see no hands. That is insane! How many of the rest of you have a prototype that is shipped to production?
00:04:17.849
So, we have a pretty good balance there. The thing is, they often say, 'Oh, 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 try to cram features into 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 shantytowns start as a quick prototype.
00:04:36.120
So, let’s go ahead and build a shantytown. It starts off pretty much like any other. Let’s say we’re all working on Giffendor, a social network for animated GIFs. The founder is a huge Harry Potter fan cosplayer; the whole thing has glasses; it’s adorable. Personally, I’m more of a Hufflepuff. This is not my website, though; it's a vanilla server-side app in Rails.
00:05:00.130
One note on pronunciation: I’m going to take a stand and say the GIF is pronounced like GitHub, `gist.` I do the same thing with JSON and `Jason`. I like to make everybody mad, mixing tabs and spaces; people love me. Alright, back to work! Let’s sprinkle in a little interactivity. Sprinkles are fun!
00:05:35.790
Yay! Your boss calls and says she wants a better experience on the site or 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. So, you look at your JavaScript file. Okay, nothing there. Undaunted, you march forward. Alright, easy right? Just make a little HTML form on the page with show/hide. All done!
00:06:05.600
Alright, so that worked, except it submits and does a full page refresh on submit. Can you do that via Ajax? It’s kind of irritating to our users. That’s what Ajax is for, right? So we submit this thing via Ajax. That seems to work, alright. But now you actually have to add that new post to the list of posts that are on that page. Alright, no problem! Okay, a little bit of a problem; we’re starting to duplicate some DOM code here in your JavaScript.
00:06:40.690
So you get to edit stuff in two places. But, you know, a little duplication; we know we don't want to duplicate - so you’re agile, you add that code right in there until this whole thing is agile. By the way, that’s a trademark of Cloud Gineering Inc. so do not use that without written permission. Ugh, so this sprinkle is starting to turn into a little more of a rain shower; a little bit of a steady rain.
00:07:14.639
But cheer up! Can we ship some software? Right? Okay. 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 they can zero out the form because if you open it, it’s just stuck there.
00:07:25.400
So dutifully, you implement the cancel button and reset 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. Well, okay, so it wasn’t super, super easy, but it was relatively straightforward. We disabled the submit button unless it’s valid and showed messages with jQuery.
00:08:06.730
This is starting to look a little weird, right? It’s starting to really come down. Poor Al Roker! Alright, so your CEO comes in to congratulate you on all the great work you’ve done! You’ve really shipped a lot of code and agile-ed everything and you just need a couple more enhancements.
00:08:34.420
Right? So let’s add an inline preview for that image so users know what they’re about to post before they post it. Also, a smart character count that doesn’t deem them for a long URL. So, now you’ve enabled or disabled submit, and 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 ship stuff!
00:09:12.610
Your business needs you to move fast and break things. You need to get stuff done (GSD), right? Craftsmanship, ship it! I say craftsmanship; it’s a good thing! You’re a cloud engineer, and you can straddle these two worlds just perfectly.
00:09:43.880
So, look at all this code that we’ve crafted! Stan talked yesterday about code shape, and the squint test is an indicator. When I squint at this code, I see a shape, and that shape is a sack of hot garbage. It’s like if you’ve ever been in, I don’t know if Chicago has this, but in Manhattan in the summertime, you get this nice smell wafting off of hot garbage. It’s great! That’s kind of how it feels.
00:10:12.010
So here we are, a total tsunami of entangled jQuery code. Let’s ship a feature against that, right? Please! It’s really important. Your CEO is now in your hands, you are a superstar developer, and the expectation is now that you just ship fast. And a like button? Let’s do it!
00:10:53.800
Okay, here’s the thing: I was going to implement this in jQuery, but there was already so much double-checking in 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. So now what do we do? Well, 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.
00:11:43.910
Addressing a ball of mud is pretty difficult. We can sweep it all under the rug or put it inside of a black box, right? You can reconstruct it like tear the whole thing down and raise it and rebuild. You can renovate block by block, which he calls keeping it working, or you can quit your job, which in some cases, I mean look, sometimes that’s the thing to do.
00:12:05.400
But a 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 like that. 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 amazing hidden lessons about your business logic – things that are encoded in very strange places. And it’s a really great way to make something that sounds like it takes two weeks, take six months.
00:12:37.930
I feel like you always discover hidden business logic. So let’s talk about a refactor; why would we do that? We decided that refactoring 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 have a bad time with it. The longer they stay on the page, the more likely they are to have problems with it.
00:13:20.260
And more importantly, to me personally, you were tossing and turning all night; you cannot sleep because you’re dreaming about the JIRA tickets that are waiting for you in the morning. And why are we still using JIRA? Why is that a thing? Anyway, separate deal, but you think about your frustrated users, and your gut tells you to fix it, and your gut is totally right.
00:13:49.550
So you’ve got to pass out of here. I think a person in this room, a super smart guy, is like, 'Why don’t you just refactor this? You’re smart; you know JavaScript objects!' And he’s largely right; that’s an idiomatic way to dig out of this. And he also asked me if I did that as a straw man for how to build really terrible jQuery code, and I was like, 'Yes, I was not doing my best.'
00:14:09.380
The other option is to use a framework to abstract away the DOM and handle data. So, 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.
00:14:34.970
So 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 with any framework that you choose. It’s just nice to kind of 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. You heard Charles mention earlier, if you were here, about why we like Ember’s model layer.
00:15:24.870
I feel like it’s a really strong model layer for the case we’re trying to accomplish. Now it has really great bindings to manage state, and it’s got a great drop-in component library to tie it all together. Alright, so step one: Let’s wrap it, wrap it, wrap it!
00:15:57.640
Okay, 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. But you can actually apply it to a lot of code; it’s really great for refactoring existing code bases toward it. So, first things first: it’s time for some justice! We’re going to put that terrible code in code jail.
00:16:36.540
And that code jail is this `initLegacyCode` function, so we’re going to create an Ember component. We’re going to put the stuff in it. Legacy code isn’t a special name, I just chose it to tell me that this is the jail. And it bootstraps the old code inside the component; we do not alter that code at all.
00:17:21.800
Also, we move the HTML into a handlebars template associated with this component. No structure of that HTML actually changes; we’re just isolating it, and then we can sprinkle it. So now, we use jQuery to stuff all of this old stuff that we had before back into the DOM.
00:17:49.580
Another thing Sandy said yesterday that is, I hope, self-evident — it made itself self-evident to me over the course of the last year or two of 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.
00:18:08.200
And 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’ll see that it’s actually not that hard to test the thing itself. We’re just going to test that it shows up and it passes.
00:18:49.850
So, I’m doing a little bit of a hand wave here over JavaScript testing setup. JavaScript test setup for getting Ember applications bootstrapped in Rails can be a little challenging. There’s a growing body of knowledge about it. On its own, JavaScript testing is pretty easy, but when you add in frameworks and tie it to Rails and add in the asset pipeline, things can get a little complicated.
00:19:32.260
It’s a little bit of a wild west, and there’s a lot of choices out there. There is 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, I don’t know what brand of toothpaste I want, I just want some toothpaste; there’s not really an accepted happy path just yet.
00:20:24.040
But it is getting better; things are maturing. We’re building schools, we’re building hospitals, still there’s a lot of need for libraries and blog posts, and people finding these happy paths. So, take me aside for 15 minutes; there are screencasts 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 sort of figuring this out, so be prepared for a little bit of rough takeoff on this deal.
00:20:51.220
Another thing is testing Ajax can feel a little intimidating when you’re trying to test JavaScript. It’s actually pretty simple; there are multiple libraries to do this kind of thing. I like I See Ajax; it’s a way to build fixtures. It intercepts Ajax calls and allows you to inject fixtures. Here we’re going to test each path through the experience. In this one, we’re testing that clicking submit shows a success message.
00:21:26.860
We just want to verify that all the things that the code says it does, it’s actually doing that. We’re not changing any code; we’re just wrapping it in tests, and that passes. So 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.
00:22:04.220
Maybe someone has another answer; we can discuss it because testing is not my favorite thing in the world. But we can now move forward with some confidence. So go ahead and sell it, victory lap; you earned it. This is, however, just the start. Now, step three is to identify models.
00:22:38.260
You have this blob of code. How do you find models in here? Well, your server MVC might give you some hints. I don’t believe that your models will map one-to-one, but it’s a great place to start.
00:23:03.280
So in this one, we actually get to start driving with a test. 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, hey, I know that I have a model, and I know part of what a model is going to do is to parse out an animated GIF URL and tell me whether it is one or is not one.
00:23:36.040
So, with no code, that thing should fail, and it does. Alright, 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 parsed URL.
00:24:01.970
It does some function, nice stuff, and then it has a little declaration at the end that says property body. It means that we’re going to depend on body, which is the blob of data that somebody just types in. The text that they type in is going to set at any time a person changes that.
00:24:37.610
That change is going to observe and update this parsed URL 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 tests passing again.
00:25:10.070
It’s the coolest feeling in the world to write some tests, take some code, like scrub some garbage out of it, do something really dangerous feeling, like jump off a cliff, and then rerun those tests, and they start passing again. You start getting green again after a refactor; it’s really fun.
00:25:36.510
And in this case, we didn’t do that much; we don’t have to write that much code because we let the framework carry our much luggage, which we cannot live without. So, the models hold onto your data; they keep it up to date and the changes in the DOM just automatically propagate out of that.
00:26:09.350
So now we’re going to lean even harder into the framework; that’s actually sort of a 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.
00:26:56.940
So in this little widget, the first state is a blank state, an initial state where the button is just visible and nothing else is. Then when we click it, the button goes away and the thing is in a ready to save state while data is in flight.
00:27:25.350
We’re going to disable the post button, leave everything else the same, and 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 because we want to reset to that initial state.
00:27:51.350
That’s kind of the business logic that we wanted in the first place, but we incrementally built it. And that’s an okay way to find out what you want, but not a great thing to ship to production, as we found. So the component starts in this initial state; we bind that state to a class on the component's DOM.
00:28:23.300
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, right? The DOM is just there to represent the app state at any given time; it’s kind of almost read-only.
00:29:02.920
So now, instead of managing the DOM with jQuery, we’re going to have buttons fire off actions that just push the state around. Like, 'You go here; you go here.' They’re sort of 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.
00:29:35.390
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 state's a model, they are in place and they’re tested, but there are a couple of ugly things left, so let’s look at it.
00:30:11.930
Co jail’s not empty. The legacy code still reaches outside the component to delete things in a sort of wonky way. Hi, how are ya? I want to rub your nose in this code even though I wrote it. What could possibly go wrong here, right? This post is pretty scary. It's painful to modify.
00:30:32.440
It invites pain for the user, but listing posts is outside the responsibility of this component we’ve created. So what can we do about it? Well, we have a pattern that we’ve already laid out: let’s create another component and sprinkle it in for listing those posts.
00:31:02.040
Another quick detour into Ember Data: At this point, I had a choice to make. Do I want to make this—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 took me only about 20 minutes to replace the model with an Ember Data model.
00:31:37.320
Ember Data is basically a wrapper that talks to Ajax for you and converts to an Ember model. It buys us lots of good things. It 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 I See Ajax just like we did before to Ember Data.
00:32:19.640
This is all the work it takes to do fixtures; it 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 has a list of those posts, and a way to delete one of them.
00:32:57.930
All I’m doing is destroying a model record. So 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. From here, we can drive out the delete functionality.
00:33:29.140
So we have a second template to replace the server-side markup, and then we sprinkle it in, just like the first one. We just say, 'Hey jQuery, take this component and stuff it into the DOM where that list of posts used to be.' Ember is going to show that for us.
00:34:03.740
Well, that’s interesting; we have two components! How are they sort of talking to each other? Well, they both react to changes in 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.
00:34:37.470
So 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.
00:35:06.840
So one thing that happened, though, is because we’re not doing stuff with jQuery UI animation stuff anymore, 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.
00:35:40.230
So remember how we bound class name to form state earlier? We said, ‘Okay, what like 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’s going to stuff that into our component every time the state changes.
00:36:25.450
So 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 kind of see — it almost tells a story — we start in an initial state with a height of 0px and an opacity of 0.
00:36:57.440
And then once it’s in another state, it actually expands. So, everything except initial will transition to that state. We transition open, divisible with a 200 millisecond delay.
00:37:25.650
It’s actually not great; I'm just going to show it to you. It’s not great to try to explain how CSS animations work, so let’s do that. I think I need to make that window smaller. It’s great though; thanks, Obama!
00:38:09.440
So we’re going to submit and now it expands. And here we’re in a validation error state, and so we have the gift preview; we have 0 characters because that smart preview is telling us that we’re not using up any characters, and we’ll post it.
00:38:27.610
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 loved very much. Okay, but instead of doing that all day, maybe we should go back to the top.
00:39:07.700
So what we’re not doing is manipulating the DOM directly. If we want, our test can relax about the DOM a little bit; we don’t necessarily need to test the framework. We can focus on application logic. We can still do integration tests if we want.
00:39:43.080
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 JAKU UCS 512-bit! It scatters your business logic across hundreds of lines of jQuery code.
00:40:17.060
So you can stop worrying about whether hackers are going to access your business logic in your front end because even your programmers can’t. That’s Cloud Gineering, folks! Also, trademark! So that turns out not to be such a super hot idea.
00:40:47.770
We've now refactored this into a place where we move that encrypted logic into an exposed state via computed properties. The business logic is actually front and center now. So we’ve gone from a sack of hot garbage to a tested and documented reliable implementation. It’s not perfect, but now it begs to be extended and reused.
00:41:15.180
From here, what happens next is really up to you. You can, from here, you’re really — actually most of the rendering that’s happening on the page is being done by Ember. So if you want to replace it and get the benefits of having a router underneath your app, great! Go nuts!
00:41:54.960
You can actually now take application handlebars and have it render those two 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, letting your components do the more dynamic pieces of your application for you.
00:42:22.780
With that pool of shared data underneath it. So now my brain is tired. Why do we do all this stuff? What even is my life? We had one job to do, right? A job that we were just about to quit.
00:42:45.760
But right before we became millionaires and the IPO, it turns out I found out a couple years ago that I quit a job just before they IPO’d. Anyway, I could be on some yacht. But we didn’t quit because we love our users, right? We love our coworkers.
00:43:05.960
And we want to love our job. Software development is really fun! It can be frustrating, but it shouldn’t give you night sweats!
00:43:35.380
So 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, stub out the data, and test that a favorite makes it favorite and an unfavorite makes it unfavorite.
00:44:14.680
Build a little favorite model that talks to the server and has many relationships. Here you can see a `des` belongs to `get post` and has many on the other side. Then we can add some dynamic content to the Handlebars template.
00:44:49.160
So 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 will demo that as well.
00:45:20.360
I’m going to switch my branch! Everybody knows this trick, right? And also, everybody knows the trick of typing ‘rails s’. It’s a little known thing; most people don’t know that’s how you load a Rails server up.
00:45:51.270
Alright, so you can see my responsive design is so badass! Whoa! Stars just fly all around! Alright, so now we can favorite this thing, and it’s actually talking to the server on the back end.
00:46:07.280
But it was so easy to do this that we were like, ‘Well, do you want me to filter this favorites for you? Because I can do that! Just show you my favorite ones.’ Oh, this one? This is legendary!
00:46:36.770
So now we have all favorites. If I unfavorite something, it will automatically pop out of the list. I’m not manually using any of this stuff; I just built a little tiny filter that took like five minutes to do.
00:47:01.690
This stuff becomes it buys you so much, and extending this actually becomes very fun. So we’ve changed our relationship now to this code base, where we actually like working on it!
00:47:40.550
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. So, I just want to tell you as a sort of an addendum to this that my personal story about my first conference was about four years ago.
00:48:07.580
And I think a lot of people can relate to this. Show of hands: Who's here that it’s pretty much their first conference? Okay, so I’m going to tell my story.
00:48:29.160
My first conference, I went to a hack night the night after the conference, and it was put on by some really great people. But I walked in, and I looked around and didn’t know anybody. I listened to some people talk, and they were smart — very well known in the community.
00:48:59.610
I was a nobody. So, I immediately turned around and left, ran out of the room as fast as I could, got in my car, and cried, and drove home. That’s pretty awesome; I’m like super a cool guy!
00:49:26.310
After that, 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.
00:50:01.600
These people in this room, if you 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.
00:50:37.000
If you can imagine what it would be like to do marketing for a multi-level marketing corporation; just imagine the existential hell! We’re pretty lucky to get to do this, and I want everybody to have as good a time writing software as I could have.
00:51:05.910
The last thing I want to say about community is we have a tendency to do this sort of tentpole thing where we raise some people up and gather around those people.
00:51:35.160
That’s not really what a community is about. Community is a mesh; it’s not like hub-and-spoke. Each person in here has something to contribute and something amazing to do. I can’t wait to go to your talks later.
00:52:09.040
I’m told I’m pretty good at hugs, so come say hi to me, and thank you so much!
00:52:30.160
You.