 All right, so I'm Nate Pendleton, and this is Tom Legier. Yeah, Legier. I always forget how to put it. But we're front-end developers here on the front-end team, mainly specializing in the editor viewer and things surrounding the diagramming infrastructure. So our goals are to show you kind of what our process has been to go from one-seven application. And in some cases, really a jQuery application, in a lot of cases, because we weren't always necessarily writing idiomatic m or even a one-seven. So a lot of our challenges have been because of jQuery and all these types of things that we'll get into in a little bit. But I'm sure as anybody had this experience upgrading a living application from one x up, how many people want to get to that? I don't know. Maybe you guys are further on. But anyway, we'll hopefully give you some good tricks and pointers toward getting there. Yeah, so. So I want to take a second and talk about where we started. It's been mentioned that we were at Ember 07 initially. Now the app is like 12 years old. By the time that we came on the scene, we had made it up to 1.7. We had done a lot of custom build logic to get that happening. Our company was based around some Java developers. And so we had a very Java-centric approach to the front end, including packaging up all of our front end assets with Maven, which was maybe the most correct way to do things. That also gave us about a three-minute iteration time on the front end. So if I made a change, hit Save, went to see that change live, it took about three minutes locally. So this was pretty slow. If I wanted to build that change somewhere that something can pick it up, we sent it off to Bamboo, our CI server. The build on Bamboo took about 12 minutes and was kind of stapled together with some custom build scripts and schlept an artifact up to Maven, and then some other little pieces off to S3. If I then wanted to consume that artifact, I would pick it up in one of our applications. So we had more than two, but basically two general deploy targets. One was our online application, which was written in play, and the other is our Atlassian integrations. Both of them also had plenty of custom build scripts. They also used Maven to consume and then produce their own artifacts. They also sported very, very slow iteration times, and they contained a ton of front end assets as overrides for the core editor piece. So basically at the end of the day, what we had was something that had a really tightly coupled front and back end, had a ton of custom logic, had a lot of duplicated code, and it just felt slow to work in and fairly fragile. Now, if we wanted to deploy this thing after having built it, consumed it, built it again, it would take another 10 minutes to upload those assets to a production server. So if you're adding up all the little clocks for our online side, it took 44 minutes if we wanted to, and this is if we're sitting on bamboo, clicking the button as soon as it comes up, 44 minutes for code locally to code in production. That was really, really slow. So does anybody else here have an app that takes a long time to deploy? Anybody else feel that pain? Yeah. Yeah, it's hard. It's frustrating. You feel like you can't respond to the changes in the world. So here's some metrics about where we started. I mentioned 1.7. Load time was a big metric for us. We clocked in at about 7 and 1 half seconds. This was something we got a lot of customer feedback about our app that needed some work. And so as we were approaching the migration, this was one of the key drivers. Our local iteration time best case for an editor-only change was about 2 and 1 half minutes. Worst case, up to 14 minutes in some of the Atlassian places. Deploy time could be well over an hour, depending on what you're doing. So that really throttled our ability to push to production. So we just weren't able to be agile. We found ourselves in a pretty slow and fragile state. So how do we make it better? We were trying to figure out what kinds of things we can do to get a rain on some of these things. And one, we're using Ember. Two, we had a wish list of a lot of different features that all of our front-end developers wanted to use, like SCSS or ES6 and all that kind of good stuff. And we didn't really have a strong direction of what we should do. So I kind of did a Google search one day and found out, Tom Sturd knows how to do a lot of the stuff in conjunction with the community and all this, using the add-ons and for SCSS support and things like that. We were able to just get really excited about, oh wait, look at all these things we can leverage if we can move up toward it. So we decided to weigh the options and we saw that Ember CLI offered fast iteration time, almost everything on our DevTech wish list. The add-ons and the community around everything are just fantastic. There's a lot of things you can do with very little configuration or effort really. And then the Ember upgrade itself gives us access to the latest documentation, makes our searches on things like stack overflow a lot more relevant because it's frustrating with an old version and you're trying to dig through stack overflow of these types of things and all the examples are for things that are completely useless too and you're frustrated because you can't use them. And all the refinements that come along with upgraded Ember too, like faster rendering, faster loading, all those great things that they keep coming out with up there. So where do you start? I mean, it's a pretty big process. We have a 10-year-old code base that is used by thousands of people every day and it's hard. You don't wanna just throw away all that business logic that people have won hard over the years and you don't wanna just sit there and let your code ride either. So it's a pretty tricky situation. You can't just stop the world, generally speaking, and just say, okay, well, we're just gonna leave it frozen until we figure this out, especially before you know what the hell you're supposed to do to get there. So what we decided to do is we had our product team decided they wanted to implement, added a commenting feature to our diagramming viewer and our editor. So what we did is we decided, okay, well, let's make a little prototype in Ember CLI. We used a sandbox test application and used Mirage to mimic the backend, which is a great tool. I don't know if anybody here uses Mirage. It's a phenomenal tool that lets you prototype your front-ends without ever implementing the lick of backend code and you can figure out what your backend interface is gonna be right from there. So definitely look at Ember CLI, Mirage. There's a hot tip. But so what we did is we developed this MVP commenting prototype in the sandbox situation and it took us about a month and everything went good. We had the iteration time we were looking for. We was like, oh, this is pretty damn sweet, cool. All right, so let's take it and let's actually implement a real use for it, which is our diagram viewer. So we decided to just go ahead and write the diagram viewer from scratch as it was fairly simple. So we just did the same thing that we did in the sandbox more or less but we added a few features to it, made it a real application rather than a test application and went from there and kind of concentrated more on the like dev and deployment processes so that we could make sure that our deployment and dev process would be as smooth as possible going into this. So that went well, learned a lot, took us about a month and we were pretty happy. We were like, okay, this is pretty great. All right, well, the next requirement was to integrate this feature with our legacy ES5 monster editor. Lots of code, lots of logic. And so this is where the pain points came in. We had to write a custom build process to eat the ES6 code and spit it back out so that we could integrate it with our already existing grunt pipeline. There was a lot of very difficult to debug integration points with lack of source maps and lack of a lot of niceties that you would get in a full-on ember app. There was a lot of bugs and it ended up taking about three months just to get that integration point in and wrap everything up, which wasn't so great compared to the experiences we had going up there. So the main takeaway we found from this and especially afterwards is don't do that. We're gonna explain what we ended up doing but this was kind of, this was suboptimal use of time in the end. We did learn a lot, we realized that we needed to harness everything in an ember CLI shell and then from there we can start decomposing and doing all those things. Yeah, so this brings us to about September of last year. We had two new features in ember CLI and they were kind of messily hacked onto our legacy ember app. We had discovered that ember CLI deploy was a really big productivity boost. It really flattened out our deployment pipeline. So we had a win there and we were using that for the viewer but we were kind of experiencing death by 1,000 cuts trying to incrementally upgrade our project. We were just trying to get a sense of what do we need to do. As an engineering team, we came to the conclusion that we needed a big project basically to overhaul our entire front-end tech stack. So whenever you want to take on a big project, you need to sell it. You need to get buy-in from other people at the company. So this is how we sold ours. We basically promised a 40% increase in developer efficiency with no performance regressions. Remember that we're pretty sensitive about our app performance and we also really wanted to make sure that management understood we had deployable apps at milestone checkpoints. So basically we're gonna make the developers a lot better but we will try and mitigate the risk by keeping things deployable at each step. What we got out of that was three months of engineering reset. We got a team of five people for three months to do nothing but working on improving our front-end experience. So that's kind of the weapon that we had going in. Thanks to the new vice president. That guy is one that came in and basically helped us with that stuff. Yeah. So I wanna take a second and talk about why milestones are important. So basically with our mantra of things are deployable at milestones, it means that if we got in over our heads, if we couldn't actually do it, if we couldn't complete the migration, we would still have something that we could show for it, something that would be useful, something that, you know, something to take away from the project. So we set some fairly aggressive deadlines but we made sure that at each milestone, we had something that we could, if everything else collapsed, we could end there and still come out ahead. Cool. So we're about to start the migration. We wrapped up a bunch of things in September, did a few spikes on some potential strategies and we started basically, I believe it was October 1st. So what fell out of that little spike sprint was that we found some starting points to our strategy. First of all, you want to touch as little code as possible while you're shoveling it over into this Ember CLI framework. You just don't want to have, you want to reduce the number of variables that go wrong, you want to do as little work as possible while getting into the framework because then your iteration time immediately starts picking up and then you can, you know, you really get a lot of quick benefits by doing it that way. We shoveled all of our old ES5 code into the vendor folders because that's basically the canonical Ember way to do things. We decided to put everything in one Ember app at first and just get our deployment processes down, get our build processes down, tests running and all that stuff so that we weren't trying to manage multiple applications at first and then we figured we could decompose them later. We separated the back end of the front end which was probably one of the biggest ones we did. It was also one of the biggest jobs. We had a lot of data getting injected as part of the front-end delivery and we needed some API endpoints to facilitate that decoupling. And we focused on build, test, deploy, just trying to get all that stuff nice and tight so that when we start having multiple applications and ramp people up onto it, we've got a nice smooth process. Yeah, so I want to take a few minutes and talk about some of the patterns that we used to actually migrate our legacy code to Ember CLI, called as Cleaning House because we had to do a lot of it. So we had a lot of start-up initialization logic. Sometimes this would be baked into templates, sometimes it was low-dependent JavaScript, but we just had a lot of this stuff. And a 10-year-old app is definitely going to accumulate some cruft. This is where a lot of ours ended up. Luckily, Ember has a fantastic pattern for dealing with that, initializers. So we would just convert all that start-up code into order-dependent initializers. And that let us get a really good handle on how to deal with it. Another pattern that we had a lot through our old code, this is pretty much how you do idiomatic legacy Ember, is a view, a controller, and a template. The view and the controller are global. So this was really the core of our real Ember code. Migrating this to a modern app is pretty simple. The controller becomes a service. The view becomes the JavaScript part of a component, and the template becomes the HTML bars template of that component. We use this pattern over and over. You have to do, you know, there's a couple of small tweaks, but for the most part you can almost just copy and paste the files. It's a very straightforward migration. We had a lot of globals. Legacy Ember is pretty much hung off of the window, and any legacy app tends to have a lot of global state. Obviously, in a modern development environment, that is to be discouraged. And so we would replace those globals with services. This lets you inject the service where it's needed, and so you aren't accumulating just a ton of global, mutable state. Finally, we had an absolute load of random templates scattered throughout our app. Just things being used as like a one-off quick way to mark up a modal or a dialogue. Excising these was a big part of migrating to CLI, and we replaced them all with components. Even if they didn't have any state, we found that there was enough hidden state baked into the app, you know, like jQuery stuff coming from other places that we wanted a place to kind of capture that. So even though you could use a partial for this, we wanted to make them all components. So speaking of jQuery. So yeah, jQuery, you know, one of the big challenges with working with moving things from a jQuery to an Ember state is that they just don't really play nice with each other. There's lots of things where they just kind of stomp on each other. The event loops are not controllable. It's really got a lot of unpredictability. The other flaws we had with some of our jQuery, in particular, which I don't think are too unique, is that we have a lot of our UI state. It's basically just encoded in the DOM. It's like it's inferred rather than explicit. And that can make things very hard to reason about, especially as you start adding more and more features. And, you know, we just had no simple single source of truth. So Ember provides that by, you know, giving you your data models and things of that nature and binding. So we have found some pretty good strategies for trying to go from jQuery style code to Ember style code. We still have quite a bit of jQuery code, but we're kind of retrofitting it as we keep going and improving things on a slower pace, feature by feature. We're doing a lot of direct DOM manipulation, which a lot of this was for just setting up the UI or for integration points with our consuming applications actually depended on, like, a certain type of selector to latch on and modify things, you know, that stuff gets pretty scary and un-maintainable and unreasonable. So we've replaced those types of things with just our simple Ember template binding. It uses the model as a single source of truth and reduces that surface area where you can get those, like, invalid states that happen in complex applications, especially in web applications. Another thing we were using a lot of were window, just straight up windows events, and those have their own inefficiencies and issues that go along with them. So for local states, it's pretty easy. Within your component, you just have an Ember action that takes care of it within. It's a little more challenging when you have multiple components, cross-component events, but we were able to use Ember's evented mix-in as a nice little stopgap. We tried to avoid this for new code, but it was definitely better than what we had previously. What you really probably want to use is data-down actions up, like they keep trying to ram into your psyche for all the Ember conferences. But it's a pretty decent stopgap in the meantime. Low-dash and underscore are useful, but they also can have their conflicts, and they're just mostly unnecessary once you combine Ember's utility functions and new ES6 features. Most of the time, you can really just do Ember dot, blah, blah, and it works a surprising amount of the time, or you can, you know, import any kind of extend or add on that you like, but generally speaking, we try to avoid those types of things because we want to refrain from importing monolithic dependencies. The last thing I want to mention is set time-out and friends. This is a pretty common thing, especially in older jQuery-style applications, because you want to cause something to happen after the next render, let's say. Well, Ember's got its own backburner event loop, so really what you want to do is schedule things onto it, whether it's time-out style, or whether you just want to say, please run this after the next render. So that's kind of the toolkit, and there's a bunch more stuff that we can talk about, but I wanted to kind of keep you brief. I want to spend a minute to talk about our milestones. As I mentioned, they were really important to get company buy-in, because they gave us basically an escape hatch. I think the milestones that we chose make sense for pretty much any Ember upgrade, going from pre-to-to post-to. So basically our first milestone was 113, which was getting everything into Ember CLI. We had to clear 112 and below deprecations, but we've left all of the 113 deprecations. Did the absolute bare minimum rewrites that we had to do to get the 113 app functional. So mostly this was schlepping everything into the vendor folder, figuring out what initialization code needed to be rewritten. And this was by far the hardest milestone to hit. But one thing that we did do right about this milestone was that we started establishing patterns early, so that really helped us out as we were moving into the later stages. Our second milestone was 2.4. We chose 2.4 because it still supports Ember legacy views, which means that you can defer rewriting all of your views until later, but you do have to take on all of your 113 deprecations. It looked really challenging. I think that we started at like 600 deprecations or something like that, but we managed to clear them out in a couple of weeks. It wasn't too bad. At this point, you have a 2.4 deployable Ember app, which is pretty great, but we wanted to get to all the way to modern Ember, which meant that we finally had to tackle rewriting all of our views. That also wasn't too bad. Honestly, they translate pretty directly to the component service pattern. We were even able to get some higher priority non-critical rewriting happen done, and the greater than 2.4 deprecations also were not too bad. We were able to, starting in October, get to this point by about the third week of December, which was pretty wonderful. Of course, we didn't quite live happily ever after that. We ran into some pretty significant issues and pains after we got to there. The first of which was probably the fact that we had all of our add-ons in separate Git repos. Now, this complicates a lot of things because you have your synchronizing versions and consuming things through an MPM thing. We had to have a private MPM server to distribute that stuff. Well, at least that's what we tried in the beginning. We then had to have the build test and merge order had to be explicit, so you're merging things into a lower-level dependency, then you've got to bring it up, bring it up, and that's not great. You also end up having a lot of pull requests for the same issue if you're touching a deeper part of the stack up through your consuming apps, which then have to be merged and vetted in a particular order. MPM linking was just nightmare. We'd have inconsistent dev environments between developers because our MPM link state was just not consistent. People really got upset with this and they were getting a little grumpy. I felt bad because I just wanted them to be happier, so I tried dumping everything into the model repos. Took it old school. Actually, this did a lot of things for us. It eliminated our pull request chains. It reduced our inconsistencies between dev environments. We were able to include our custom dev scripts, which aren't too much, but they handle MPM linking and initializing your dev environment. We were able to include those directly in the repo, which really helps with onboarding and synchronizing workspaces amongst team members. We did run in some tricky bits with Travis, but now that they've got these things called build stages and we had Mr. VP over there throw a bunch of money at them for a lot of concurrent jobs, we actually work real great now with all that, but there might be some better bang for your buck solutions out there if you wanted to use a monorepo, just something to keep in mind if you go that way. That was our upgrade. After we got to the monorepo, that was pretty much the extent of our migration and we've been reaping the benefits ever since. This is what our stack looks like now. We have all of our front end code in the front end. Our Ember version is 211 and we can upgrade as we please. Our local iteration time is way, way faster. For some things, it's like two or three seconds. For other things in the editor, it's like 15 or maybe even 20 seconds, but it's so much better. We have a consistent build. We can leverage the entire Ember ecosystem now. We can go to EmberConf and come back with a basket of new ideas about what to do and actually implement some of it, which is awesome. We've been able to ship features and even apps in the time since the migration. We have been building our stuff on Travis CI. We run all of our tests in parallel. Our test time is way down. If tests pass on Merge's Demaster, we deploy an artifact. We're able to do that completely independently at the back end, which has really cut down on both our front end deploy time and our back end deploy time. Our apps look way cleaner. We don't have any maven dependencies. We've cut out a ton of front end build scripts. There's no more front end in the back end, so we've actually cut out a ton of duplicate code and we're experiencing much better iteration times even on those back end apps. Finally, to actually deploy this stuff, it's immediate. As long as the revision's already been pushed, activating it, it takes seconds. We are really much better at being agile now. We can push code from our machine to production in 12 minutes now when it took 45 on a good day before. Here's the before and after. It ended at Ember 211. One thing we didn't really talk about was how the load time improved. We got in 210 and 211 rendering improvements, compiled template weight drop, and we, as I mentioned, ended up deleting a decent amount of duplicated code between the back end overrides of the front end. That led to a much faster initial load. Iteration time is much better. Deploy time is much better, and that means that we can deploy to production way, way, way more. So what do we learn? The main things that we really wanted to focus on and you remembering coming out of here are the five following things. For migration over integration, try to keep everything moved into the Ember CLI framework before you start trying to monkey around too much with the details. Start in one big old app so that you don't have to rejigger your deployment scripts, your test scripts, and that kind of thing. Touch as little as possible. Try to reduce the number of variables that can go wrong while you're doing this stuff because plenty of things are going to go wrong anyway. You don't need to make it any more confusing as it's already going to be. Figure out common patterns that you can use throughout your team so that you have a consistent approach and people can collaborate in an easier way. Make sure you have sensible milestones. You can make sure you're making that progress and if any business emergencies show up or whatever may happen, you can at least get some benefit out of the work you have done and hopefully come back to it in a later time. And that's all we've got for you. We've got our vice president of engineering, James Rowley. So I just want to thank these guys. They've done an amazing job. Oh, yeah. Questions? Anybody? We're happy to have you. Huh? Oh, sure. Yeah. Five. Well, four. Some stages were two, some stages were five. How many engineers do we have for the migration? So we had allocated, I think, a team of four, but we got an extra bonus engineer for a month at the end. Our attorney-generic team, honestly, was 12 people. It's 16 now. In addition to add-ons that you mentioned, like morage. So we... Question. The question is, is there any other add-ons, besides morage, that we were able to use and upgrade that were a boon to our team? We've used quite a few. The most recent one is we were using less as opposed to SAS. And it turns out less just isn't very well supported for a lot of things. And we were trying to use some component scope. We wanted component scoping. We wanted a few other things. So we did start to leverage SAS in that way recently so that we have component scope things. It's just got much more support throughout the add-on world. It ended up we had to do a lot of custom configuration. It just didn't really work out for us. We found a really nice tool called less to SAS that kind of automated that conversion for us. So that's a sweet pointer if you're looking to doing that. Yeah, Ember concurrency, Ember CLI deploy has been huge. Like just worth its weight in gold. And a lot of Ember CLI deploy plugins to pretty much do whatever we need to. I'm trying to think if we have any other killer... So we use the Mocha and Chai test framework instead of the default Q unit. But we still have a couple of older projects that are still in Q unit. So we've still got to sync all those up a little. The storybooks? Oh, yeah. There was this other one that we've been starting to create on our new project called Freestyle. And it kind of gives you a living style guide. Basically gives you a kind of automatically generated web page so that you can put all of your components on it, have your designers use that as their style guide. And actually turns out that if your components are self-contained you can actually use them within the style guide and see everything about them. Which is pretty cool actually. And it takes almost no configuration. So that's a pretty sweet tool. Ember fetch. And then we also want to do Ember engines. That's like something kind of down the path but something we're really excited about. So the question was when we separated our back end and front end. Did we keep the old way of serializing data or did we do it in JSON API? We tried to touch the back end as little as possible. So we have a pretty thick layer of adapters and serializers that do all of our translation for us. For new developed stuff we are developing it using JSON API. But for the old stuff, yeah, we have a kind of a set of patterns and some generic stuff that kind of gets us between that old REST style and newer JSON API style. Yeah, the more consistent your old one it works. The easier that is, the old stuff is going to have a few little things. That's a pretty challenging thing itself. So the question was, did we have to stop our development while we were of new features as we did this? The answer is that we did little spikes on the side while we were still developing new features. But then we ended up getting the buy-in for the three months. During that three months we had only, I think, a couple of little bug fixes that went out. But for the most part we were all hands on deck, focused on that, which was a huge benefit because it would have been much more difficult to try to have running features and this going at the same time. So we got really lucky in that we were able to convince our uppers, our superiors, and our business guys, the check riders, the check men, the money men, to give us that time. It was just clear it was going to be just by a thousand paper cuts over the next 18 months. It was something we had to do. We had affordable versions of almost every library that we used. So we were going to have to take the hit and luckily we got the support. Jack and Riley, our team, took three months. We guaranteed what we were going to perform in that three months. We had performance boost in our application as well as a huge productivity increase for all of our developers and it was painful. Like our turnaround time, I did a one-line fix. I mean it was, you know, it was too desperate state for us that we had to get to. But we delivered on every piece of it and we got this far. We'd always have the buildable product that was completely QA that we could go live with. We couldn't go to the next phase. So we kind of delivered from that and we got the authority to do it and we used those three months like... We took a full manage. We took a full manage of having three months like no features and that's pretty rare in a company. Especially a small company. They were like, okay, this makes sense. We'll stop. And I'm like, really? And like, do you want a PowerPoint for that? And he's like, no, you just did your whole spiel. So we just did. That was great. Yeah, and so if you're going to take this presentation to your check writers, like, do it. You, yes, it's 100% worth it. It was rare for 25 years of being a VP Engineering. It was like, wow, really? I wouldn't be that surprised if we've already kind of paid that efficiency back. But I'm not going to go that far. Any other questions? Testing, good. How did that change? Oh, we did. We wrote tests. Okay, so the question is, how do you reconcile 1.7 or 1.7 and before testing with a modern number, which has a pretty strong emphasis on testing? So we kind of, we're working on it. We did have a lot of tests written for our 1.7 stuff, actually. Unfortunately, they were all run through grunt and written in Jasmine, neither of which plays particularly well with Ember CLI. One of the big reasons why we migrated from QUnit to MoCA is that it's a lot easier to convert Jasmine tests to MoCA tests than it is to convert Jasmine tests to QUnit tests. So one of our goals upcoming is to reconcile those, to basically migrate that. We have gotten a lot stronger at writing front-end tests now. We're doing, on a lot of things like acceptance tests first or acceptance tests along with the feature that we're building, but at the very least, we're laying a strong foundation of integration tests. Yeah, and we've also done some things like add code coverage to our process so that those get uploaded and document generation and all that kind of stuff. Keep us honest. Yeah, and truthfully, our new services, services are pretty easy to test generally. So our core services, we've been more or less hitting 100% coverage on those. And then as the leafs get smaller, they get less tested by that. We're getting better about getting our component tests down and automated. Acceptance tests both pre-build and post-build. We have a separate system for post-build. Acceptance tests that our QA lead takes care of. Mr. Oves. So we kind of have a dual prong strategy on acceptance tests because we're still figuring out the best way to do it. But I think they're kind of complementary. It's a learning journey. Anybody else? Last question. Last question. You modified food twice in a single render. On which code example? Well, no, no, I'm just saying that this is... Yeah. So how did you deal with here? So the question was, there's a deprecation that gets thrown. You modified food twice in a single render or I think there's even a variant that's like you modified food after, or in did insert. Yeah, did insert. Yeah. So we just used the run loop to get around it. We pretty much wrapped the whole callback in an after render. Yeah. Exactly. So that kind of goes to the point of like make it work then figure it out. It's one of the things. We did it all over the place and we decided to not rewrite all over the place and instead try and just get it work. But things that we've retouched since then we definitely have kind of retrofitted. We have like a flaw in something that we have ported over and we're like, okay well why don't we just fix this little thing up and make it a component. So we're doing nice little touches and leaving it better than we've found. You can't do everything else.