1 00:00:18,330 --> 00:00:18,950 LUKE MELIA: All right, folks. 2 00:00:18,950 --> 00:00:24,040 Thanks for coming out. So you are at Lightning Fast 3 00:00:24,040 --> 00:00:26,180 Deployment of Your Rails-Baked Javascript APP. 4 00:00:26,180 --> 00:00:29,349 Hopefully you're in the right place. 5 00:00:29,349 --> 00:00:32,369 My name is Luke. I, Luke Melia. I live 6 00:00:32,369 --> 00:00:35,540 in Manhattan in New York City. Got a couple 7 00:00:35,540 --> 00:00:39,140 little girls who are learning Ruby and Javascript using 8 00:00:39,140 --> 00:00:44,050 Code Academy and KidsRuby. And I have a company 9 00:00:44,050 --> 00:00:46,980 called Yapp that I co-founded. We're one of these 10 00:00:46,980 --> 00:00:51,110 kind of hybrid product and consulting companies. And when 11 00:00:51,110 --> 00:00:54,080 I'm not doing Dad stuff or coding, I really 12 00:00:54,080 --> 00:00:57,000 love to play beach volleyball and have recently taken 13 00:00:57,000 --> 00:00:58,809 up parkour. 14 00:00:58,809 --> 00:01:01,549 So Yapp Labs is the consulting side of our 15 00:01:01,549 --> 00:01:04,290 business. We do Ember.js consulting and training based out 16 00:01:04,290 --> 00:01:08,000 of New York and Seattle, if, if that's interesting, 17 00:01:08,000 --> 00:01:09,900 happy to talk with you about that. 18 00:01:09,900 --> 00:01:12,850 So, by way of introducing this topic, I want 19 00:01:12,850 --> 00:01:16,130 to tell you a story. And it's a story 20 00:01:16,130 --> 00:01:21,840 of when deployments were driving me crazy. You know, 21 00:01:21,840 --> 00:01:24,530 kind of like, tear my hair out crazy. 22 00:01:24,530 --> 00:01:27,390 We had an app, and the app consisted, you 23 00:01:27,390 --> 00:01:29,280 know, it was pretty straightforward for a modern app. 24 00:01:29,280 --> 00:01:30,510 And it was a Rails app that had a 25 00:01:30,510 --> 00:01:33,510 home page. It had your terms and conditions page. 26 00:01:33,510 --> 00:01:35,570 You can't have a site without that. It had 27 00:01:35,570 --> 00:01:38,450 a Javascript app, which in this case was an 28 00:01:38,450 --> 00:01:40,660 Ember app, but, you know, you can substitute any 29 00:01:40,660 --> 00:01:44,280 kind of rich Javascript MVC app that you, style 30 00:01:44,280 --> 00:01:46,680 you'd like, for the purposes of this talk. I 31 00:01:46,680 --> 00:01:49,450 then, of course, had a JSON API. 32 00:01:49,450 --> 00:01:50,960 And so these, this was kind of the bullet 33 00:01:50,960 --> 00:01:53,870 points, but in terms of the amount of code, 34 00:01:53,870 --> 00:01:55,280 the complexity, and how much time that it took 35 00:01:55,280 --> 00:01:57,870 working on it, it was more like this, right. 36 00:01:57,870 --> 00:02:00,090 We had a lot of work on the Javascript 37 00:02:00,090 --> 00:02:02,409 app. Some, a bunch more in the JSON API. 38 00:02:02,409 --> 00:02:04,440 And the rest of the site was, you know, 39 00:02:04,440 --> 00:02:07,200 pretty trivial. 40 00:02:07,200 --> 00:02:09,470 But in terms of deployments, every time we wanted 41 00:02:09,470 --> 00:02:11,840 to, every time I made a change and wanted 42 00:02:11,840 --> 00:02:15,340 to deploy it, we would package everything up and 43 00:02:15,340 --> 00:02:18,430 deploy it. And so I have a question for 44 00:02:18,430 --> 00:02:21,260 you folks. Hopefully everybody's, in the room has worked 45 00:02:21,260 --> 00:02:22,930 with Rails. How long does it take to deploy 46 00:02:22,930 --> 00:02:24,930 a Rails app? We're gonna do a show of 47 00:02:24,930 --> 00:02:26,950 hands, and by the end, I hope everybody will 48 00:02:26,950 --> 00:02:28,959 have their Rails app, sorry, will have their hands 49 00:02:28,959 --> 00:02:29,800 up. 50 00:02:29,800 --> 00:02:32,480 And so, please start by raising your hand if 51 00:02:32,480 --> 00:02:34,730 your Rails app is deployed in less than thirty 52 00:02:34,730 --> 00:02:37,890 seconds. OK. Good awesome. One, I want to talk 53 00:02:37,890 --> 00:02:42,000 with you later. How about less than one minute? 54 00:02:42,000 --> 00:02:47,000 Cool. A few folks. Less than three minutes? A 55 00:02:47,000 --> 00:02:50,520 bunch more. Less than five minutes? Keep, keep your 56 00:02:50,520 --> 00:02:51,730 hands up even if you were in the early 57 00:02:51,730 --> 00:02:53,060 group. Less than five minutes. 58 00:02:53,060 --> 00:02:56,069 OK, so we're probably at a majority now. Less 59 00:02:56,069 --> 00:03:01,750 than ten minutes? Keep your hands up. OK. And 60 00:03:01,750 --> 00:03:03,569 less than twenty minutes? I hope that's everybody in 61 00:03:03,569 --> 00:03:06,580 the room. Please, please, mercy. OK. Cool. 62 00:03:06,580 --> 00:03:09,650 So, the, I think the, the answer is, it 63 00:03:09,650 --> 00:03:11,180 takes at least a few minutes to deploy a 64 00:03:11,180 --> 00:03:14,030 Rails app, unless you're one of an exceptional few 65 00:03:14,030 --> 00:03:16,920 folks in the audience. And I get it. There's 66 00:03:16,920 --> 00:03:19,530 a lot, you know, there's files to transfer. There's 67 00:03:19,530 --> 00:03:22,980 dependencies to install. Most modern Rails apps, you know, 68 00:03:22,980 --> 00:03:25,010 that I run into, that we create, have a 69 00:03:25,010 --> 00:03:28,150 lot of gem dependencies. It takes some time to 70 00:03:28,150 --> 00:03:30,520 boot the app with all those dependencies and just 71 00:03:30,520 --> 00:03:33,090 with, just with your app code. 72 00:03:33,090 --> 00:03:35,620 And so, that's fine, except for, I was going 73 00:03:35,620 --> 00:03:38,760 days just working on the Javascript app. Right, I 74 00:03:38,760 --> 00:03:41,950 was just making changes in Javascript, and every time 75 00:03:41,950 --> 00:03:44,770 I wanted to deploy, I was waiting five minutes 76 00:03:44,770 --> 00:03:50,540 in our case to just deploy static Javascript changes. 77 00:03:50,540 --> 00:03:52,050 And it really made me want to throw something 78 00:03:52,050 --> 00:03:53,959 across the room. Why was I doing this to 79 00:03:53,959 --> 00:03:55,030 myself? 80 00:03:55,030 --> 00:03:58,510 And it wasn't just me that I was annoying. 81 00:03:58,510 --> 00:04:02,430 I was also annoying our users, because, in most, 82 00:04:02,430 --> 00:04:06,680 in most Rails deployment scenarios, there are some, there 83 00:04:06,680 --> 00:04:09,200 can be some hiccups in each deploy. And we'll, 84 00:04:09,200 --> 00:04:11,230 so let's talk a little bit about this, this 85 00:04:11,230 --> 00:04:16,339 kind of hiccups and deployments and zero downtime deploys. 86 00:04:16,339 --> 00:04:19,060 So if your Rails app takes several seconds to 87 00:04:19,060 --> 00:04:22,449 boot, which is probably about average, obviously it can't 88 00:04:22,449 --> 00:04:25,830 serve requests during that time. And so, under high 89 00:04:25,830 --> 00:04:28,650 load in most architectures, most requesters are just gonna 90 00:04:28,650 --> 00:04:30,620 be queued, waiting for the app to be ready 91 00:04:30,620 --> 00:04:34,090 to handle requests. And then once it boots up, 92 00:04:34,090 --> 00:04:37,139 it's gonna start handling those requests, and eventually flush 93 00:04:37,139 --> 00:04:41,039 that queue and hopefully catch up to the requests 94 00:04:41,039 --> 00:04:43,210 as they're coming in. 95 00:04:43,210 --> 00:04:45,919 And so users that are hitting the, hitting your 96 00:04:45,919 --> 00:04:49,139 app during this time may experience at, at best 97 00:04:49,139 --> 00:04:51,289 case just a couple of seconds of downtime. At 98 00:04:51,289 --> 00:04:52,889 worst case, kind of a feeling like that, that 99 00:04:52,889 --> 00:04:55,330 this site is not responsive. 100 00:04:55,330 --> 00:04:57,520 And so it disappoints me that we don't yet 101 00:04:57,520 --> 00:05:01,800 have a kind of conventional solution for zero downtime 102 00:05:01,800 --> 00:05:03,990 deploys. But it kind of makes sense because, by 103 00:05:03,990 --> 00:05:08,490 definition, Rails runs inside of other web servers, and 104 00:05:08,490 --> 00:05:10,550 so, and that, this is really a concern kind 105 00:05:10,550 --> 00:05:11,889 of at that web server layer. 106 00:05:11,889 --> 00:05:15,300 So, Heroku, for example, has an experimental solution. Heroku 107 00:05:15,300 --> 00:05:20,330 Labs is, Heroku's kind of unsupported experimental area features. 108 00:05:20,330 --> 00:05:23,539 And you can run heroku labs:enable preboot, which will 109 00:05:23,539 --> 00:05:25,830 start up new servers or dynos with your new 110 00:05:25,830 --> 00:05:28,669 code, wait three minutes to give your Rails app 111 00:05:28,669 --> 00:05:30,979 plenty of time to boot, and then switch traffic 112 00:05:30,979 --> 00:05:32,650 over. 113 00:05:32,650 --> 00:05:36,680 For, if you're using Puma or Unicorn, there are 114 00:05:36,680 --> 00:05:39,419 facilities to start one worker at a time, or 115 00:05:39,419 --> 00:05:42,169 groups of workers by sending signals to the master 116 00:05:42,169 --> 00:05:45,020 process. HAProxy is a tool that I've used in 117 00:05:45,020 --> 00:05:48,229 the past to kind of split traffic, give yourself 118 00:05:48,229 --> 00:05:52,389 time to boot up another, another set of servers. 119 00:05:52,389 --> 00:05:54,499 HAProxy is nice because you can do health checks 120 00:05:54,499 --> 00:05:57,150 against those new servers and say, am I ready 121 00:05:57,150 --> 00:06:02,289 to deploy? And Passenger also has some solutions around 122 00:06:02,289 --> 00:06:04,589 this. 123 00:06:04,589 --> 00:06:06,339 In terms of kind of the full scope of 124 00:06:06,339 --> 00:06:08,619 zero downtime stuff, it gets more complicated when you 125 00:06:08,619 --> 00:06:12,569 talk about database migrations and what's safe and what's 126 00:06:12,569 --> 00:06:16,430 not safe for these, these types of, these types 127 00:06:16,430 --> 00:06:18,949 of deployments. And I'm happy to chat about that 128 00:06:18,949 --> 00:06:21,039 with anybody later, but that's out of scope for, 129 00:06:21,039 --> 00:06:22,729 for this particular talk. 130 00:06:22,729 --> 00:06:24,360 What I do want to drill into a little 131 00:06:24,360 --> 00:06:28,080 bit is issues with static assets and zero downtime 132 00:06:28,080 --> 00:06:30,219 deploys, because that's the thing that was, you know, 133 00:06:30,219 --> 00:06:31,589 kind of at the heart of what I was 134 00:06:31,589 --> 00:06:35,099 doing, was really dealing with these static Javascript assets. 135 00:06:35,099 --> 00:06:37,539 And I think that these issues aren't discussed often. 136 00:06:37,539 --> 00:06:38,839 So I kind of want to drill down, kind 137 00:06:38,839 --> 00:06:41,479 of at a detailed level, and talk about them 138 00:06:41,479 --> 00:06:43,419 here. 139 00:06:43,419 --> 00:06:46,279 So when a browser makes an initial request to 140 00:06:46,279 --> 00:06:49,479 your server for, to load your, your rich Javascript 141 00:06:49,479 --> 00:06:52,099 app, it's loading the index dot html file and 142 00:06:52,099 --> 00:06:54,899 by index.html, I'm gonna refer to the html file 143 00:06:54,899 --> 00:06:58,089 that's the bootstrapping, it's bootstrapping your Javascript app. It's 144 00:06:58,089 --> 00:07:00,069 the thing that has a little bit of code 145 00:07:00,069 --> 00:07:01,629 to fire things up and it pulls in the 146 00:07:01,629 --> 00:07:03,839 right Javascript and CSS assets. 147 00:07:03,839 --> 00:07:06,189 So the request comes into your servers, your server 148 00:07:06,189 --> 00:07:09,679 responds with the HTML file, with the text slash 149 00:07:09,679 --> 00:07:13,580 html mine type, and typically the, your asset files, 150 00:07:13,580 --> 00:07:15,729 the Javascript and CSS that are gonna be referenced 151 00:07:15,729 --> 00:07:19,479 here, are gonna be fingerprinted. Right, so we do, 152 00:07:19,479 --> 00:07:21,699 take a hash of the contents of the Javascript 153 00:07:21,699 --> 00:07:24,909 file, we set it as a, we include that 154 00:07:24,909 --> 00:07:27,409 hash in the file name, and we're then are 155 00:07:27,409 --> 00:07:30,149 able to set far features expires headers on those 156 00:07:30,149 --> 00:07:32,520 files, so that when the file changes, we don't 157 00:07:32,520 --> 00:07:35,809 have to worry about cache expire or anything. We 158 00:07:35,809 --> 00:07:38,749 just gotta new file that's gonna come through as 159 00:07:38,749 --> 00:07:41,179 if the browser's never seen it before. 160 00:07:41,179 --> 00:07:43,599 And so in this case, our html file might 161 00:07:43,599 --> 00:07:47,599 contain something like assets slash app dash abc123 dot 162 00:07:47,599 --> 00:07:51,779 js, where abc123 is this fingerprint we're talking about. 163 00:07:51,779 --> 00:07:54,830 And so the browser takes that html, parses the 164 00:07:54,830 --> 00:07:57,469 page, a short time later makes the request for 165 00:07:57,469 --> 00:08:01,550 app dash abc123. Server says, here you go, some 166 00:08:01,550 --> 00:08:05,330 text Javascript. Browser parses that, boots up the app. 167 00:08:05,330 --> 00:08:06,369 All is well. 168 00:08:06,369 --> 00:08:08,849 Hopefully this is very clear to everybody who's in 169 00:08:08,849 --> 00:08:12,029 the room. What's maybe less clear, unless you've thought 170 00:08:12,029 --> 00:08:14,740 about it in detail is that during deployments, this 171 00:08:14,740 --> 00:08:17,300 idea can break down a little bit. And so 172 00:08:17,300 --> 00:08:20,459 imagine that we've got our deployment and we've got 173 00:08:20,459 --> 00:08:23,319 two kind of sets of server. The top set 174 00:08:23,319 --> 00:08:25,300 that we're looking at here on the screen is 175 00:08:25,300 --> 00:08:27,749 the existing code. The bottom set is the new 176 00:08:27,749 --> 00:08:29,869 code that you're deploying. 177 00:08:29,869 --> 00:08:31,020 And in this case, there's a change to the 178 00:08:31,020 --> 00:08:35,080 Javascript file, so there's the new fingerprinted filename there. 179 00:08:35,080 --> 00:08:38,120 So when our initial request for our page comes 180 00:08:38,120 --> 00:08:40,830 in, it was, it will go to the old 181 00:08:40,830 --> 00:08:43,020 code, because we haven't yet switched traffic over to 182 00:08:43,020 --> 00:08:46,890 the, to the new deploy. And just like in 183 00:08:46,890 --> 00:08:49,209 our first example, it's gonna come back with the 184 00:08:49,209 --> 00:08:54,220 index file references app dash abc123 dot js. 185 00:08:54,220 --> 00:08:56,490 Now, what if, in that moment, where, as the 186 00:08:56,490 --> 00:08:59,710 page is being parsed, before this request comes back, 187 00:08:59,710 --> 00:09:02,000 we then switch traffic over to pointing to the 188 00:09:02,000 --> 00:09:05,510 new server? Well, request is gonna come in for 189 00:09:05,510 --> 00:09:09,060 app dash abc123 dot js, the new server gets 190 00:09:09,060 --> 00:09:11,640 the request and says, ah, I don't know what 191 00:09:11,640 --> 00:09:13,950 you're talking about. I don't have a Javascript file 192 00:09:13,950 --> 00:09:18,230 with that name. And so it says, 404 Not 193 00:09:18,230 --> 00:09:18,900 Found. 194 00:09:18,900 --> 00:09:24,130 And this is a challenging problem to, to address, 195 00:09:24,130 --> 00:09:26,240 because there's a, because of a few reasons. One 196 00:09:26,240 --> 00:09:28,280 is that most simply if I, at this point, 197 00:09:28,280 --> 00:09:30,900 now hit refresh in the browser, of course everything 198 00:09:30,900 --> 00:09:33,460 works fine. Right, because now both of those requests 199 00:09:33,460 --> 00:09:35,380 are going to the new server and the world 200 00:09:35,380 --> 00:09:36,200 is good. 201 00:09:36,200 --> 00:09:39,640 It can be further kind of shadowed, this issue, 202 00:09:39,640 --> 00:09:43,090 because, if you are serving your assets up through 203 00:09:43,090 --> 00:09:46,000 an assets host at CBN, you might have some, 204 00:09:46,000 --> 00:09:48,730 some, some of their, your edge nodes might have 205 00:09:48,730 --> 00:09:51,510 that old page cached. That old Javascript cached. In 206 00:09:51,510 --> 00:09:54,450 which case, those nodes will return it just fine. 207 00:09:54,450 --> 00:09:57,850 And so, to, to know that you're, you know, 208 00:09:57,850 --> 00:10:00,910 totally impervious to this, you know, might be a 209 00:10:00,910 --> 00:10:02,620 little bit fuzzy, and also to be able to 210 00:10:02,620 --> 00:10:05,350 reproduce it reliably is a challenging thing to do. 211 00:10:05,350 --> 00:10:08,320 But, in short, to avoid these hiccups, both the 212 00:10:08,320 --> 00:10:10,660 old versions of your assets and the new versions 213 00:10:10,660 --> 00:10:12,420 have to be available for at least a few 214 00:10:12,420 --> 00:10:15,670 minutes during your deployment in order to, to make 215 00:10:15,670 --> 00:10:19,450 this zero downtime approach really work well on the 216 00:10:19,450 --> 00:10:21,730 static asset front. 217 00:10:21,730 --> 00:10:23,330 And so this was one of the things I 218 00:10:23,330 --> 00:10:25,670 was thinking about during these many five minute deploys, 219 00:10:25,670 --> 00:10:29,540 where I was, you know, wishing that I had 220 00:10:29,540 --> 00:10:32,340 a solution. And so, in thinking about that, I 221 00:10:32,340 --> 00:10:34,040 said, well, we could figure out how to do 222 00:10:34,040 --> 00:10:35,810 this on our app servers. Kind of keep the 223 00:10:35,810 --> 00:10:38,830 old versions of the, of the Javascript and the 224 00:10:38,830 --> 00:10:40,610 new versions together. Or we could move the assets 225 00:10:40,610 --> 00:10:43,220 elsewhere. And the idea of moving the assets elsewhere 226 00:10:43,220 --> 00:10:45,780 really appealed to me, because that meant, if they 227 00:10:45,780 --> 00:10:47,870 weren't on the Rails, my Rails servers, then maybe 228 00:10:47,870 --> 00:10:50,060 I could avoid doing Rails deploys when I just 229 00:10:50,060 --> 00:10:52,580 had static asset changes. 230 00:10:52,580 --> 00:10:55,140 So I started to sketch out an idea. We've 231 00:10:55,140 --> 00:10:56,580 got our Rails server at the top, and we 232 00:10:56,580 --> 00:10:59,220 had this separate static asset server at the bottom. 233 00:10:59,220 --> 00:11:01,590 Let's deploy our Rails app code to the Rails 234 00:11:01,590 --> 00:11:05,860 servers, our Javascript, CSS, and images to these static 235 00:11:05,860 --> 00:11:08,800 assets servers, and then we would deploy our index 236 00:11:08,800 --> 00:11:13,070 file. Where would we deploy our index file? 237 00:11:13,070 --> 00:11:14,880 And so, I started to think about, well, what 238 00:11:14,880 --> 00:11:16,000 is the index file? It is kind of this 239 00:11:16,000 --> 00:11:18,240 thing that bridges the two? What are the requirements 240 00:11:18,240 --> 00:11:20,490 around it? What do we know about it? And 241 00:11:20,490 --> 00:11:24,070 this index, the html file points to fingerprinted Javascript 242 00:11:24,070 --> 00:11:27,250 and CSS, but it's not fingerprinted itself. That's obviously 243 00:11:27,250 --> 00:11:30,600 important, because it needs to be at, at a 244 00:11:30,600 --> 00:11:34,530 consistent location for browsers to locate, to load. It 245 00:11:34,530 --> 00:11:38,260 contains Javascript urls and code to boot the Javascript 246 00:11:38,260 --> 00:11:40,470 app to load CSS and such in the right 247 00:11:40,470 --> 00:11:44,080 order. It's a good place to provide environment-specific configuration 248 00:11:44,080 --> 00:11:46,960 to Javascript. Maybe you have some differences between dev 249 00:11:46,960 --> 00:11:49,520 and stage in production. 250 00:11:49,520 --> 00:11:51,150 One thing that I knew was key, because I 251 00:11:51,150 --> 00:11:53,250 had struggled with it, is that when you serve 252 00:11:53,250 --> 00:11:55,480 this html off of the same domain as your 253 00:11:55,480 --> 00:11:59,090 API, life is way simpler with respect to cores 254 00:11:59,090 --> 00:12:03,910 and cross origin security issues. And finally, if I 255 00:12:03,910 --> 00:12:06,140 wanted, if you wanted to be able to deploy 256 00:12:06,140 --> 00:12:08,920 changes quickly to your users, caching on this particular 257 00:12:08,920 --> 00:12:11,960 page should be minimal to none, so that you 258 00:12:11,960 --> 00:12:14,810 can pretty instantly switch over. 259 00:12:14,810 --> 00:12:17,920 So my conclusion, from thinking about this, is that 260 00:12:17,920 --> 00:12:20,190 the html page ideally should be managed and thought 261 00:12:20,190 --> 00:12:23,290 about as part of your static asset deployment process, 262 00:12:23,290 --> 00:12:25,350 but it should be served off of your Rails 263 00:12:25,350 --> 00:12:26,240 server. 264 00:12:26,240 --> 00:12:28,720 And, as importantly, it should be served off your 265 00:12:28,720 --> 00:12:33,210 Rails server without requiring a Rails reboot or redeploying 266 00:12:33,210 --> 00:12:36,760 the entire Rails app. And so, so we were 267 00:12:36,760 --> 00:12:39,000 able to start to refine this sketch and say, 268 00:12:39,000 --> 00:12:41,620 OK, our Rails server's gonna be serving up our 269 00:12:41,620 --> 00:12:45,100 API requests, our, kind of, traditional, dynamic Rails pages 270 00:12:45,100 --> 00:12:47,160 as part of the html for a Javascript app, 271 00:12:47,160 --> 00:12:49,490 and our static asset server's gonna be serving up 272 00:12:49,490 --> 00:12:51,860 the Javascript, CSS and images. 273 00:12:51,860 --> 00:12:53,740 And so that means we need to somehow deploy 274 00:12:53,740 --> 00:12:58,520 our html up to the Rails server without a 275 00:12:58,520 --> 00:13:00,360 full, a full reboot. And so, how could we 276 00:13:00,360 --> 00:13:01,070 do this? 277 00:13:01,070 --> 00:13:03,400 Well, the most obvious thing to me was, well, 278 00:13:03,400 --> 00:13:06,330 take a html file and put it on the 279 00:13:06,330 --> 00:13:09,590 file system of each Rails server. And this has 280 00:13:09,590 --> 00:13:11,610 a few things that aren't great about it. You 281 00:13:11,610 --> 00:13:15,310 can probably make this work in some configurations. In 282 00:13:15,310 --> 00:13:19,370 many deployment environments, disk is ephemeral, and so relying 283 00:13:19,370 --> 00:13:22,290 on, you know, on copying some things up might 284 00:13:22,290 --> 00:13:24,120 not be a great idea. It's also a little 285 00:13:24,120 --> 00:13:27,440 bit weird to mix assets, files deployed from a 286 00:13:27,440 --> 00:13:31,030 particular gitshaw, with files deployed from somewhere else, kind 287 00:13:31,030 --> 00:13:33,700 of in the same file system. 288 00:13:33,700 --> 00:13:35,450 And so, we said, well, what if there's something 289 00:13:35,450 --> 00:13:37,780 that we could all see and talk to? Well, 290 00:13:37,780 --> 00:13:40,900 what about uploading to S3? So then all the 291 00:13:40,900 --> 00:13:44,860 Rails servers can, can see S3, read from it, 292 00:13:44,860 --> 00:13:47,010 be able to serve up that html. And this 293 00:13:47,010 --> 00:13:48,930 could kind of work, but reading from S3 is 294 00:13:48,930 --> 00:13:52,310 a little bit slow. And we wanted this page 295 00:13:52,310 --> 00:13:55,790 to be fast, obviously. No Javascript or CSS is 296 00:13:55,790 --> 00:13:57,930 gonna start being loaded until this page is loaded 297 00:13:57,930 --> 00:13:59,790 in the browser. And so the fastest we could, 298 00:13:59,790 --> 00:14:01,060 the faster we could get this page to the 299 00:14:01,060 --> 00:14:03,370 user, the better. 300 00:14:03,370 --> 00:14:05,620 And so, then we said, well, what about redis? 301 00:14:05,620 --> 00:14:09,560 Redis is persistent. It's fast. For us, it was 302 00:14:09,560 --> 00:14:13,190 already in our environment. We liked this idea a 303 00:14:13,190 --> 00:14:16,000 lot. We decided to, to dig in. This is 304 00:14:16,000 --> 00:14:17,750 not the, as you'll see, this is not the 305 00:14:17,750 --> 00:14:21,029 only way to do it. It's totally possible to 306 00:14:21,029 --> 00:14:23,750 user other systems besides redis. But redis kind of 307 00:14:23,750 --> 00:14:26,960 firt the bill for us and works quite well, 308 00:14:26,960 --> 00:14:27,710 as you'll see. 309 00:14:27,710 --> 00:14:29,880 So, the general idea was we're gonna deploy into 310 00:14:29,880 --> 00:14:32,490 redis and then serve out of redis via a 311 00:14:32,490 --> 00:14:35,960 Rails controller. So here's the simplest possible kind of 312 00:14:35,960 --> 00:14:39,470 deploy code that we had. It's a rig task, 313 00:14:39,470 --> 00:14:42,760 and we're going to generate our html for the 314 00:14:42,760 --> 00:14:46,380 current assets, and that's, that's kind of an exercise 315 00:14:46,380 --> 00:14:48,110 for your build tooling, which we'll talk about a 316 00:14:48,110 --> 00:14:50,440 little bit later. And then once we had this 317 00:14:50,440 --> 00:14:52,090 html, we're actually going to set it as a, 318 00:14:52,090 --> 00:14:56,029 at a, as a redis key-value store. So the 319 00:14:56,029 --> 00:14:57,880 html is the value and the key would be 320 00:14:57,880 --> 00:15:02,730 something well-known like jsapp colon index, for example. And, 321 00:15:02,730 --> 00:15:04,840 and this is a redis connection that's connecting directly 322 00:15:04,840 --> 00:15:07,779 to your charted deployment environment. So that's staging more 323 00:15:07,779 --> 00:15:10,440 production. 324 00:15:10,440 --> 00:15:13,090 Once it's there in redis, our controller, again, the 325 00:15:13,090 --> 00:15:16,470 most simplest version, is get the value out of 326 00:15:16,470 --> 00:15:21,350 redis. Render text.html. Now, when I first looked at, 327 00:15:21,350 --> 00:15:22,910 looked at this code or wrote this code, I 328 00:15:22,910 --> 00:15:24,410 said, is that gonna be served up with the 329 00:15:24,410 --> 00:15:26,570 right mine type? Seems a little strange. And it 330 00:15:26,570 --> 00:15:29,920 turns out that, yes, if you do render text 331 00:15:29,920 --> 00:15:33,070 and some string, Rails serves that up with text 332 00:15:33,070 --> 00:15:36,690 slash html, if you don't specify. So, it's a 333 00:15:36,690 --> 00:15:38,520 little, I think a little bit of a confusing 334 00:15:38,520 --> 00:15:41,430 API, but it does what we want. 335 00:15:41,430 --> 00:15:44,370 So, we can now continue to refine this approach. 336 00:15:44,370 --> 00:15:46,370 We know we're deploying, when we need to deploy 337 00:15:46,370 --> 00:15:49,160 Rails app code, we're doing a deployment to our 338 00:15:49,160 --> 00:15:52,370 Rails server. When we're deploying Javascript, CSS and images, 339 00:15:52,370 --> 00:15:54,870 we're deploying to the static assets server, and then 340 00:15:54,870 --> 00:15:58,160 we're deploying html by connecting to redis and deploying 341 00:15:58,160 --> 00:15:59,430 into it. 342 00:15:59,430 --> 00:16:03,430 And we can make things a little bit nicer 343 00:16:03,430 --> 00:16:06,520 by dropping cloud front right in front of, by 344 00:16:06,520 --> 00:16:08,810 using S3 as our static assets server, and then 345 00:16:08,810 --> 00:16:10,940 dropping cloud front instead of in front of our, 346 00:16:10,940 --> 00:16:13,350 in front of S3. So, for very little effort 347 00:16:13,350 --> 00:16:15,620 and very little money, we've got now CBN distribution 348 00:16:15,620 --> 00:16:18,170 for our static assets. 349 00:16:18,170 --> 00:16:21,710 Now, there's a few things about this deployment to 350 00:16:21,710 --> 00:16:25,160 S3, in terms of making it fast. Getting a 351 00:16:25,160 --> 00:16:28,720 file list from S3 can be somewhat expensive, particularly 352 00:16:28,720 --> 00:16:31,839 the more files that you have. And so the 353 00:16:31,839 --> 00:16:34,440 approach that we took was to generate a manifest 354 00:16:34,440 --> 00:16:38,620 file of our current assets and store the copy 355 00:16:38,620 --> 00:16:42,000 of that manifest on S3 so we, we're basically 356 00:16:42,000 --> 00:16:44,500 gonna read the remote manifest, compare it to our 357 00:16:44,500 --> 00:16:46,310 local manifest, and know we only need to deploy 358 00:16:46,310 --> 00:16:48,520 what's different. And so this means that if I 359 00:16:48,520 --> 00:16:51,540 make one Javascript, one line of Javascript change, it's 360 00:16:51,540 --> 00:16:54,510 just the file that that's concatenated into that needs 361 00:16:54,510 --> 00:16:56,620 to be updated, and not all of my images 362 00:16:56,620 --> 00:17:00,260 and CSS, as we're doing our deploy, our assets 363 00:17:00,260 --> 00:17:02,110 deploy to S3. 364 00:17:02,110 --> 00:17:05,260 Now, purging has been on our to-do list for 365 00:17:05,260 --> 00:17:08,209 this architecture for quite a while, right. After a 366 00:17:08,209 --> 00:17:12,000 deploy is successfully completed, we can, in theory, remove 367 00:17:12,000 --> 00:17:15,039 stuff from S3. We never really got around to 368 00:17:15,039 --> 00:17:17,959 that. Mostly because it's so incredibly cheap to store 369 00:17:17,959 --> 00:17:20,730 these small files on S3, so, still on our 370 00:17:20,730 --> 00:17:23,638 to-do list. I would probably not recommend you prioritizing 371 00:17:23,638 --> 00:17:26,839 it too high for yourself. The code for this 372 00:17:26,839 --> 00:17:29,730 S3 sink is here at this link. I will 373 00:17:29,730 --> 00:17:31,399 make these slides available. You don't have to worry 374 00:17:31,399 --> 00:17:34,100 about copying it down. This repo is open source 375 00:17:34,100 --> 00:17:35,570 and contains a lot of the code that we're 376 00:17:35,570 --> 00:17:37,860 looking at today. And it's the actual code that 377 00:17:37,860 --> 00:17:41,169 we use for our, for our production environments. 378 00:17:41,169 --> 00:17:45,230 So, once you start thinking about this architecture, it 379 00:17:45,230 --> 00:17:47,799 paves the way to do something a little bit 380 00:17:47,799 --> 00:17:50,119 more fundamental with your rich Javascript app and your 381 00:17:50,119 --> 00:17:53,179 Rails app, which is that you pull them, tease 382 00:17:53,179 --> 00:17:56,179 them apart into separate repositories. And now why would 383 00:17:56,179 --> 00:17:57,340 you want to do that? 384 00:17:57,340 --> 00:17:59,470 Well, one of the reasons is, you know, thinking 385 00:17:59,470 --> 00:18:02,289 about tagging, do, you know, kind of tagging a 386 00:18:02,289 --> 00:18:04,840 deployed version of your code. Since you've got these 387 00:18:04,840 --> 00:18:08,360 independent deploy processes, it makes sense to be able 388 00:18:08,360 --> 00:18:11,230 to tag a Javascript deploy separate from a Rails 389 00:18:11,230 --> 00:18:13,559 deploy, because they really are now independent of each 390 00:18:13,559 --> 00:18:14,619 other. 391 00:18:14,619 --> 00:18:17,679 And I find also that thinking of your Javascript 392 00:18:17,679 --> 00:18:21,570 app as a separate, independent client of your API, 393 00:18:21,570 --> 00:18:22,850 works really well. Kind of puts it on the 394 00:18:22,850 --> 00:18:25,249 same level as a native app, for example, maybe 395 00:18:25,249 --> 00:18:26,769 if you've got an iPhone app that communicates to 396 00:18:26,769 --> 00:18:29,289 your API as well. 397 00:18:29,289 --> 00:18:33,289 It also opens up the realm of possibility to 398 00:18:33,289 --> 00:18:35,029 having a lot of flexibility with what kind of 399 00:18:35,029 --> 00:18:37,200 build tools you want to use with your Javascript 400 00:18:37,200 --> 00:18:40,899 app. You may choose to use sprockets in your 401 00:18:40,899 --> 00:18:44,119 separate standalone repo. Or you may choose to use 402 00:18:44,119 --> 00:18:49,119 grunt, gulp, broccoli, brunch. You name it, there's obviously 403 00:18:49,119 --> 00:18:52,509 a lot of innovation and creativity happening around build 404 00:18:52,509 --> 00:18:55,619 tools in the Javascript environment. And my guess would 405 00:18:55,619 --> 00:18:59,369 be that you're, we're gonna see faster, you know, 406 00:18:59,369 --> 00:19:03,749 innovation, iteration in the Javascript environment for building Javascript 407 00:19:03,749 --> 00:19:06,019 apps than we will in the Ruby environment for 408 00:19:06,019 --> 00:19:07,340 building Javascript apps. 409 00:19:07,340 --> 00:19:11,710 So, we've now got an approach that works pretty 410 00:19:11,710 --> 00:19:14,700 well. But I think the, the question is, is 411 00:19:14,700 --> 00:19:16,639 it worth doing the work to set this in 412 00:19:16,639 --> 00:19:18,820 place? Like, how fast is this in the real 413 00:19:18,820 --> 00:19:20,559 world? And so I took one of our apps, 414 00:19:20,559 --> 00:19:22,980 and this isn't, was not a scientific bench mark, 415 00:19:22,980 --> 00:19:27,039 so consider it directional. And our builds took about 416 00:19:27,039 --> 00:19:29,909 six and a half seconds. This particular app is 417 00:19:29,909 --> 00:19:31,669 using Rake Pipeline as a build tool for the 418 00:19:31,669 --> 00:19:37,799 Javascript side. Our transferring assets to S3 using this 419 00:19:37,799 --> 00:19:40,269 differential approach was about a second, and then uploading 420 00:19:40,269 --> 00:19:44,330 html into redis was about two and a half 421 00:19:44,330 --> 00:19:47,649 seconds. And so, instead of a five minute deploy, 422 00:19:47,649 --> 00:19:50,289 I was now able, we were now able to 423 00:19:50,289 --> 00:19:53,759 deploy this, our Javascript apps in under ten seconds. 424 00:19:53,759 --> 00:19:58,139 So just by that, that was a big win. 425 00:19:58,139 --> 00:20:00,429 And stopped me from wanting to throw things across 426 00:20:00,429 --> 00:20:03,149 the office. But, I think that, you know, in 427 00:20:03,149 --> 00:20:06,619 any kind of architectural choices like this, you learn 428 00:20:06,619 --> 00:20:09,200 if this is a good idea or not over 429 00:20:09,200 --> 00:20:12,200 time, right, based on, how does, how does this 430 00:20:12,200 --> 00:20:15,039 architecture respond to changes. What kind of possibilities does 431 00:20:15,039 --> 00:20:16,909 it enable? So I want to talk a little 432 00:20:16,909 --> 00:20:19,639 bit about the kind of emergent behavior that we've 433 00:20:19,639 --> 00:20:21,580 seen around, now that we've had this in production 434 00:20:21,580 --> 00:20:23,580 for awhile. 435 00:20:23,580 --> 00:20:26,239 The first thing is, the idea of preview. And 436 00:20:26,239 --> 00:20:28,950 so this is an actual command line session for 437 00:20:28,950 --> 00:20:31,929 deploying an app. In this case, it's yapp-prefs, which 438 00:20:31,929 --> 00:20:36,259 is our, kind of, account settings app. We first 439 00:20:36,259 --> 00:20:40,779 run rake dist. This is the build. And with 440 00:20:40,779 --> 00:20:42,580 the build completes, in this case, as we saw 441 00:20:42,580 --> 00:20:44,289 in the pie chart before, in about six seconds 442 00:20:44,289 --> 00:20:47,600 or so. And it says, OK, to deploy these 443 00:20:47,600 --> 00:20:52,529 assets to S3, run rake deploy:assets with this, what 444 00:20:52,529 --> 00:20:55,070 we call a manifest idea, this b35b. 445 00:20:55,070 --> 00:20:57,769 So what's a manifest id? We talked earlier about 446 00:20:57,769 --> 00:21:00,820 fingerprinting assets and we talked about creating a manifest 447 00:21:00,820 --> 00:21:03,289 file. So what we do also is we fingerprint 448 00:21:03,289 --> 00:21:05,129 the manifest file. So we take a hash of 449 00:21:05,129 --> 00:21:07,570 the contents of that manifest file and we say, 450 00:21:07,570 --> 00:21:10,210 OK, that is the manifest id for this deploy. 451 00:21:10,210 --> 00:21:12,249 And that's, it's, it's kind of a unique identifier 452 00:21:12,249 --> 00:21:15,399 as it's going through its unique deploy process. 453 00:21:15,399 --> 00:21:17,489 And so we run rake deploy:assets, which does the 454 00:21:17,489 --> 00:21:20,580 differential upload to S3 and it's gonna show us 455 00:21:20,580 --> 00:21:23,029 what it uploads. It's gonna spit out, OK, I've 456 00:21:23,029 --> 00:21:28,899 uploaded these four files. JS, CSS, two yaml files 457 00:21:28,899 --> 00:21:31,149 for the manifest. We, actually, these are two copies 458 00:21:31,149 --> 00:21:33,070 of the same thing. One is a, has a 459 00:21:33,070 --> 00:21:35,139 file name with the id, and one just is, 460 00:21:35,139 --> 00:21:38,470 hey I am the latest. And it's going to 461 00:21:38,470 --> 00:21:40,989 then tell us the next command to run is 462 00:21:40,989 --> 00:21:45,659 deploy:generate_index for this manifest id. And what this is 463 00:21:45,659 --> 00:21:49,049 gonna do is going to connect to redis and 464 00:21:49,049 --> 00:21:51,509 set this at a key named for the manifest 465 00:21:51,509 --> 00:21:54,369 id. So in the previous simplistic example we looked 466 00:21:54,369 --> 00:21:57,289 at, it was just updating jsapp index. Now it's 467 00:21:57,289 --> 00:21:59,779 updating a key at prefs, in this case, prefs:index 468 00:21:59,779 --> 00:22:04,039 b35b something, you know, named for the manifest id. 469 00:22:04,039 --> 00:22:06,989 And why is this awesome? Well, this command line 470 00:22:06,989 --> 00:22:10,429 tool can now tell us, hey, to preview this, 471 00:22:10,429 --> 00:22:12,610 this asset change, go ahead and take a look 472 00:22:12,610 --> 00:22:16,509 at your at, your site with the query param 473 00:22:16,509 --> 00:22:21,629 manifest id equals b35 et cetera. And what this 474 00:22:21,629 --> 00:22:24,110 is gonna do is it's going to pull the 475 00:22:24,110 --> 00:22:27,080 new html file from redis. Gonna show, which is 476 00:22:27,080 --> 00:22:29,899 loading up the new Javascript. It's connecting to the 477 00:22:29,899 --> 00:22:33,190 production API. So you're able to smoke test this 478 00:22:33,190 --> 00:22:36,039 in production. Everything is working just as the user 479 00:22:36,039 --> 00:22:38,659 will see it, except for your users don't see 480 00:22:38,659 --> 00:22:40,879 it yet. So if you screwed something up, you've 481 00:22:40,879 --> 00:22:42,580 got a chance before kind of pulling the trigger 482 00:22:42,580 --> 00:22:44,889 and switching it life to go. 483 00:22:44,889 --> 00:22:47,720 And then, finally, one more command to kind of 484 00:22:47,720 --> 00:22:49,779 active that redis key and make it the current 485 00:22:49,779 --> 00:22:53,659 key. And so what does this code look like? 486 00:22:53,659 --> 00:22:55,519 It's actually not that much more complicated than what 487 00:22:55,519 --> 00:22:59,200 we saw before. We invoke our rake task with 488 00:22:59,200 --> 00:23:04,730 the manifest id. Generate our html file. And then, 489 00:23:04,730 --> 00:23:07,549 instead of setting jsapp index, we'll set a jsapp 490 00:23:07,549 --> 00:23:09,529 key based on the manifest name, or redis key 491 00:23:09,529 --> 00:23:13,299 based on the manifest name. And then spit out 492 00:23:13,299 --> 00:23:15,700 something to give the developer the url to take 493 00:23:15,700 --> 00:23:20,480 a look at, to preview, to preview the app. 494 00:23:20,480 --> 00:23:23,409 On the server-side, we're gonna add one more redis 495 00:23:23,409 --> 00:23:27,529 request to the mix. If there's a manifest id 496 00:23:27,529 --> 00:23:30,899 param, then we'll just use that. If it's blank, 497 00:23:30,899 --> 00:23:33,269 then we'll go and we'll connect to a current 498 00:23:33,269 --> 00:23:35,509 key. Grab the manifest id, then use that to 499 00:23:35,509 --> 00:23:40,070 serve up the current version of, of the site. 500 00:23:40,070 --> 00:23:41,659 Of the, of this index file. 501 00:23:41,659 --> 00:23:46,119 And so, that has been pretty powerful, and it's, 502 00:23:46,119 --> 00:23:48,320 it's a super useful tool that we use in 503 00:23:48,320 --> 00:23:51,289 almost every single deploy. The developer's just gonna do 504 00:23:51,289 --> 00:23:53,789 a quick smoke test and say, yes, everything looks 505 00:23:53,789 --> 00:23:56,429 good, before they flip the switch. 506 00:23:56,429 --> 00:24:00,999 The next interesting aspect that this kind of enabled 507 00:24:00,999 --> 00:24:05,109 is around dynamic html rewriting. And so what we 508 00:24:05,109 --> 00:24:08,749 realize is that as html was passing through from 509 00:24:08,749 --> 00:24:11,950 redis through the controller back to the browser, we 510 00:24:11,950 --> 00:24:14,899 had the opportunity to make adjustments if we wanted 511 00:24:14,899 --> 00:24:18,929 to. And one category of adjustment that we ended 512 00:24:18,929 --> 00:24:22,259 up making, as you see in this example, is 513 00:24:22,259 --> 00:24:24,889 injecting some information about the current user. 514 00:24:24,889 --> 00:24:27,960 Now, obviously in a Rails controller we know typically 515 00:24:27,960 --> 00:24:31,409 who the current user is. Most apps will have 516 00:24:31,409 --> 00:24:35,100 a current_user method available to any controller that they 517 00:24:35,100 --> 00:24:38,590 can, it can grab. In contrast, when you're booting 518 00:24:38,590 --> 00:24:41,950 up a Javascript MVC app, at that point, you 519 00:24:41,950 --> 00:24:44,159 know, most apps don't know who the current user 520 00:24:44,159 --> 00:24:46,320 is. And if, in most, you know mostly there 521 00:24:46,320 --> 00:24:49,330 is some XHR request that's involved in figuring out, 522 00:24:49,330 --> 00:24:51,499 is this user logged in and, if so, what 523 00:24:51,499 --> 00:24:53,970 is their role in the system? And during that 524 00:24:53,970 --> 00:24:56,869 time, the user is sitting there waiting, right. The 525 00:24:56,869 --> 00:24:58,999 Javascript is just kind of, maybe it's rendering a 526 00:24:58,999 --> 00:25:01,889 loading spinner for the user or something. But it's 527 00:25:01,889 --> 00:25:04,489 a little bit annoying and it also makes the 528 00:25:04,489 --> 00:25:07,799 boot process for the, for your Javascript app more 529 00:25:07,799 --> 00:25:08,179 complicated. 530 00:25:08,179 --> 00:25:11,200 So we said, well what if the app could 531 00:25:11,200 --> 00:25:15,129 have, at boot time, have access to this information? 532 00:25:15,129 --> 00:25:17,600 So what we're doing here is, in the controller 533 00:25:17,600 --> 00:25:20,509 action at the top, between the time we get 534 00:25:20,509 --> 00:25:24,519 the html out of redis and return, render it, 535 00:25:24,519 --> 00:25:27,669 we're going to inject current_user information. We're gonna grab 536 00:25:27,669 --> 00:25:31,919 the current_user and then user our AcitveModel serializer to 537 00:25:31,919 --> 00:25:35,249 convert it to JSON, escape it, and stick it 538 00:25:35,249 --> 00:25:37,159 into the head tag. 539 00:25:37,159 --> 00:25:39,690 And you'll notice that the method that we're using 540 00:25:39,690 --> 00:25:42,720 to add this to the html, you might find 541 00:25:42,720 --> 00:25:46,419 a little crude. And certainly when I first started 542 00:25:46,419 --> 00:25:48,720 doing this, I said, OK, we'll use Nokogiri. We'll 543 00:25:48,720 --> 00:25:51,799 parse the html. And then we'll insert, you know, 544 00:25:51,799 --> 00:25:55,399 insert a node, and then we'll render, you know, 545 00:25:55,399 --> 00:25:58,279 convert that back to text. And it turns out 546 00:25:58,279 --> 00:26:00,889 that really what we're doing is so simple that, 547 00:26:00,889 --> 00:26:02,929 as fast as Nokogiri is, and it's pretty fast 548 00:26:02,929 --> 00:26:05,820 for an XML or html parser, it is not 549 00:26:05,820 --> 00:26:08,759 faster than string manipulation and string indexing. 550 00:26:08,759 --> 00:26:10,830 And so, in this case, we're just looking at, 551 00:26:10,830 --> 00:26:12,059 where's the end of the head tag or the 552 00:26:12,059 --> 00:26:15,309 beginning of the head tag, inject this meta tag 553 00:26:15,309 --> 00:26:18,350 in there. And this works great. 554 00:26:18,350 --> 00:26:20,739 The, some other use cases for this same approach 555 00:26:20,739 --> 00:26:24,950 might be injecting csrf tokens if you, you know, 556 00:26:24,950 --> 00:26:27,669 need to interact with Rails forms from, from your 557 00:26:27,669 --> 00:26:32,309 Javascript app. Including dynamic analytics params, we had a 558 00:26:32,309 --> 00:26:36,210 case where the, the Javascript app was kind of 559 00:26:36,210 --> 00:26:39,129 the, the, a goal page for a Google analytics 560 00:26:39,129 --> 00:26:41,960 kind of flow. And we needed to set some, 561 00:26:41,960 --> 00:26:45,220 certain Javascript only in some conditions. So this was 562 00:26:45,220 --> 00:26:46,499 a nice way to do that. 563 00:26:46,499 --> 00:26:49,049 If you're using feature flags through something like the 564 00:26:49,049 --> 00:26:53,980 excellent rollout gem, it's great that that's available throughout 565 00:26:53,980 --> 00:26:55,450 Rails. But how do you make it available inside 566 00:26:55,450 --> 00:26:57,789 of your Javascript app also, this is a nice 567 00:26:57,789 --> 00:27:00,389 solution to be able to kind of inject variables 568 00:27:00,389 --> 00:27:03,799 along those lines. 569 00:27:03,799 --> 00:27:07,039 Another pretty awesome thing that we've been able to 570 00:27:07,039 --> 00:27:09,529 do using this approach is doing A/B testing within 571 00:27:09,529 --> 00:27:13,639 our Javascript app. And we've experimented with two different 572 00:27:13,639 --> 00:27:17,220 kinds. One is, kind of, setting some flags based 573 00:27:17,220 --> 00:27:19,840 on which bucket the user ends up in, A 574 00:27:19,840 --> 00:27:23,359 or B. This is pretty similar to what we 575 00:27:23,359 --> 00:27:26,950 just described. And then the, the second is serving 576 00:27:26,950 --> 00:27:29,600 up wholly different html based on the A/B bucket. 577 00:27:29,600 --> 00:27:33,249 We'll, I'll talk, take you through each of those. 578 00:27:33,249 --> 00:27:36,859 So this is using the split gem, which, if 579 00:27:36,859 --> 00:27:39,210 you haven't seen, is a great A/B tool, A/B 580 00:27:39,210 --> 00:27:42,419 test tool for Ruby frameworks, web frameworks. It has 581 00:27:42,419 --> 00:27:45,590 a Rails integration that gives us the A/B test 582 00:27:45,590 --> 00:27:48,619 method, which you see on line eleven there. And 583 00:27:48,619 --> 00:27:52,470 so we're doing some injection into our html where 584 00:27:52,470 --> 00:27:54,649 we're saying, if the user is part of the 585 00:27:54,649 --> 00:27:58,840 show walkthrough experiment, then we're going to inject the 586 00:27:58,840 --> 00:28:01,720 script tag that just sets a global variable. And 587 00:28:01,720 --> 00:28:04,429 then our Javascript app as it boots in is 588 00:28:04,429 --> 00:28:06,229 running, is, is, can consult that global variable to 589 00:28:06,229 --> 00:28:08,989 decide whether to show the walkthrough that you're doing, 590 00:28:08,989 --> 00:28:10,090 doing some testing on. 591 00:28:10,090 --> 00:28:13,759 And then later, elsewhere in your app, you would 592 00:28:13,759 --> 00:28:16,830 indicate that, that the goal was achieved, that the 593 00:28:16,830 --> 00:28:19,720 user signed up or published or whatever it is 594 00:28:19,720 --> 00:28:22,950 that was kind of the, the goal for you 595 00:28:22,950 --> 00:28:25,830 A/B test. And so this was great. This is 596 00:28:25,830 --> 00:28:28,249 excellent for the kinds of A/B tests where both 597 00:28:28,249 --> 00:28:32,830 paths are supported by your, by a given incarnation 598 00:28:32,830 --> 00:28:34,700 of your Javascript app. 599 00:28:34,700 --> 00:28:38,649 We had another case, though, where, this was right 600 00:28:38,649 --> 00:28:41,259 around the time that iOS7 released and there was, 601 00:28:41,259 --> 00:28:43,289 if you recall, there was a lot of excitement 602 00:28:43,289 --> 00:28:46,389 in, around flat design. And so we did a 603 00:28:46,389 --> 00:28:49,139 redesign of our Javascript app. We weren't immune to 604 00:28:49,139 --> 00:28:54,559 the hype. And, but as we got near completion, 605 00:28:54,559 --> 00:28:56,789 we started to wonder, well, we're gonna release this. 606 00:28:56,789 --> 00:28:58,009 How do we know that this is any better 607 00:28:58,009 --> 00:29:00,809 than our existing site? Is this going to improve 608 00:29:00,809 --> 00:29:04,119 our metrics or hurt them? And we wanted to 609 00:29:04,119 --> 00:29:06,129 try to get some confidence one way or the 610 00:29:06,129 --> 00:29:06,769 other. 611 00:29:06,769 --> 00:29:08,929 So we said, well, we've been developing this in 612 00:29:08,929 --> 00:29:10,950 a branch, and we already know that we can 613 00:29:10,950 --> 00:29:13,669 preview different versions of our Javascript app, because we 614 00:29:13,669 --> 00:29:16,090 had this preview mechanism in place. Could we use 615 00:29:16,090 --> 00:29:18,350 this for A/B testing, also? 616 00:29:18,350 --> 00:29:21,249 And so, it turns out that we were able 617 00:29:21,249 --> 00:29:23,840 to just update our deploy scripts to be able 618 00:29:23,840 --> 00:29:27,450 to deploy from a branch into an experiment kind 619 00:29:27,450 --> 00:29:30,129 of redis key, and then use the same A/B 620 00:29:30,129 --> 00:29:33,929 test mechanism to determine, for the new design experiment, 621 00:29:33,929 --> 00:29:36,190 should the user use the current manifest or the 622 00:29:36,190 --> 00:29:38,989 experimental manifest? 623 00:29:38,989 --> 00:29:41,019 Once we made that decision, we then got the 624 00:29:41,019 --> 00:29:45,440 appropriate manifest id from redis, got the html, rendered 625 00:29:45,440 --> 00:29:48,499 it, and users either saw our old app or 626 00:29:48,499 --> 00:29:52,029 the new flat-design app. Turns out, flat design, about 627 00:29:52,029 --> 00:29:55,919 nine percent better. So good news. 628 00:29:55,919 --> 00:29:58,259 So this is suitable for changes where your development's 629 00:29:58,259 --> 00:30:00,210 capping at a branch, and you want to A/B 630 00:30:00,210 --> 00:30:03,070 between the branches. Not a common scenario, but if 631 00:30:03,070 --> 00:30:05,429 it's, when it's, it's useful, it was great to 632 00:30:05,429 --> 00:30:09,830 see how this approach supported that architecture. 633 00:30:09,830 --> 00:30:11,960 One more possibility that I didn't cover here, but 634 00:30:11,960 --> 00:30:16,759 you can imagine how this might work, is around 635 00:30:16,759 --> 00:30:20,239 doing rollback. So what if every time that we 636 00:30:20,239 --> 00:30:23,590 did this deploy and updated our html in redis 637 00:30:23,590 --> 00:30:26,259 we pushed into a redis list and said, hey, 638 00:30:26,259 --> 00:30:29,309 so and so user deployed such and such manifest 639 00:30:29,309 --> 00:30:31,940 id at this time, and then we were able 640 00:30:31,940 --> 00:30:33,979 to have a rake task that reads that list 641 00:30:33,979 --> 00:30:37,299 and lets you rollback to any particular version. Hopefully, 642 00:30:37,299 --> 00:30:39,460 at this point, you can see how straightforward that 643 00:30:39,460 --> 00:30:42,200 would, that kind of thing would be as well. 644 00:30:42,200 --> 00:30:44,039 So, with that, I want to say thank you 645 00:30:44,039 --> 00:30:47,409 to my colleagues at Yapp Labs who helped create 646 00:30:47,409 --> 00:30:50,769 this. Kris Seldon, Stefan Penner and Ray Cohen. And 647 00:30:50,769 --> 00:30:52,229 while we were working on this, we had heard 648 00:30:52,229 --> 00:30:56,769 some rumors about some Square engineers doing a, using 649 00:30:56,769 --> 00:30:59,369 a similar approach at Square. So we took some 650 00:30:59,369 --> 00:31:01,899 inspiration from those rumors as well, and so thank 651 00:31:01,899 --> 00:31:05,210 you, nameless Square engineers, or if anybody's here. Love, 652 00:31:05,210 --> 00:31:07,879 love to chat with you about it. 653 00:31:07,879 --> 00:31:11,820 We've got some time for questions, and so I 654 00:31:11,820 --> 00:31:14,409 want to open it up to all of you. 655 00:31:14,409 --> 00:31:15,789 Question in the middle? 656 00:31:15,789 --> 00:31:17,009 All right, cool. Thank you all so much. Appreciate 657 00:31:17,009 --> 00:31:19,429 it. Enjoy the rest of the conference.