All aboard Ember: How we ensured app robustness in our migration

In the previous blog of this series on our migration to Ember, we had shed some light on the migration of a legacy codebase and the associated challenges. In this post, we will focus on the robustness of the application, which would include:

  • Maintenance of the Ember application with periodic migration to the latest LTS.
  • Frameworks and tools used in the SPA(Single Page Application).
  • Deployment changes for the EmberCLI-Rails gem.
  • CI and tools used for error-free deployment.

Continuous Ember LTS upgrades and maintenance:

When we started the migration from legacy, we’d been on Ember version 3.4. We then decided to migrate to 3.10 to utilize the latest features such as angle bracket syntax for Ember components, packaged imports, etc. We built a few modules in 3.10 and once the Ember Octane edition was rolled out, we migrated to 3.16 LTS to get the advantages of Glimmer components, tracked properties, native class-based syntax, etc. Since the Octane migration itself was backwards compatible and our codebase was a little smaller, we were able to migrate easily with the code mods and migration guide provided by the Ember community with minimal changes, and did not require manual changes for every individual file. Since most of the add-ons provided Octane support, we did not face much of an issue. 

Getting the team ready with the new framework & tools:

As a standard practice, we hold internal webinars and sessions on the evolving architecture and notify the team on new tools, conventions and standards so that onboarding developers to the Ember codebase becomes easier. We faced some challenges while context switching between ES6 and legacy code for reviews. But, we were able to adapt, eventually. Developers were given on-the-job training on Ember while building the new features and added functionalities. Slowly, they started fixing lint issues and then started writing test cases, and so on.

Deployment changes

One of the main challenges while migrating the new modules to Ember is the deployment. Thoughtbot’s EmberCLI-Rails gem, by default, supports the rails deployment which in turn will trigger the Ember deployment. However, we did not want to load the Ember app from the app server. We wanted to leverage the S3 to load the static page after deployment. We had an option to bypass this by using ember-CLI-deploy, but as a security best-practice we did not want to burn the secret key as part of the deployment. This left us with the only option of overriding the default behaviour of the EmberCLI-Rails gem. We have made the below changes in the initializer of the app. We have checks to distinguish the environment so that we can bypass the Ember build triggered by the gem for the staging and production environment.

Once it is done, we have to notify the gem to find the file in S3. To do that we modify the root_path and dist_path to the S3 URL, which is fetched from the config.

$ember_asset_URL denotes your CDN bucket URL

The leg of the config is to map the index file of the built Ember project. This is done in two steps.

First, after the rails deployment is done, we run the script manually to trigger the Ember build and move the entire folder to the S3 bucket. Second, to distinguish between multiple deployments, a commit hash or MD5 can be appended to the file. This URL is then injected into the gem code.

Voila! when an ember route is loaded inside the RAILS page, the index file is fetched and rendered.

Tools and CI:

  • Translation Rake task: One of the tedious things to maintain while having an Ember app inside RAILS is the support for i18n (internationalization). Maintaining duplicate files and copying those values would be a mammoth task. We decided to automate this by creating a rake task. It ensures that the existing keys in the RAILS code (.yml) are copied to the Ember environment (.json) without manual intervention. This saves a large amount of development time.
  • Test case coverage: An important aspect is to make the application free from functional defects. This is done by identifying them in the early stage of the lifecycle by maximising the test coverage of code. We achieved this by mandating the unit-test case coverage for every single Pull request. The CI runs on the PR and if the test coverage has new errors, then it is marked as ‘build failed’ and a mailer notification is sent.
  • Linter: Another aspect of creating clean code is to keep a regular check on it. By clean I mean readable, accessible, semantically correct code. Linting the code as a part of the build is one of the most commonly used methods to do this. We have enabled linting as a part of GitHub action that notifies the PR creator of any discrepancies from the rule list.
  • Bundle size Check: Adding multiple modules and components as the product grows is a common thing to happen. However, that also means that we need to keep track of the app size, which otherwise would become slow, clunky, and provide a poor page loading experience. One way to ensure that we create reusable components and optimised code is to make sure that the bundle size is always within a threshold for a particular count of modules. We have included the ember-cli-bundlesize add-on to track this. For every PR that is raised, this is run by CI and notifies the PR creator.

We are migrating the RAILS app to the Ember app, module by module. We still have a long way to go.

And hence, this is one more step forward in our journey of migrating the entire rails app to an ember app progressively.

With this final part, we conclude this series on our migration journey to Ember. It’s been a great learning experience, and we’ll return with a ‘fresh’ series on similar challenges in our endeavour to make delight easy. Watch this space!

Co-Authors:  Dinesh Kumar & Vishnu Balaji.

Cover design: Vignesh Rajan