Thoughts on Improving Developer Ergonomics for emberjs

“Convention over Configuration” is an often called mantra when working with ember.js. Especially new ember developers really embrace this, when they start working with a zero-configuration ember. A unified system is very helpful also for long term ember developers that jump onto a new project and immediately feel home.
Unfortunately this is a totally different story when you start configuring your ember app or addon. That part actually is “Configuration over Convention”, I call this territory the wild wild west of ember.

I created ember-cli-create, because I was experimenting with Module Unification a lot during summer. First I was becoming tedious to pass environment variables and arguments and later annoyed by that, because I did it fairly often and like always missed some of them.
I realized that the potential for ember-cli-create is way bigger than I initially thought. Despite providing a nicer UI wizard to start your ember project, it also can tackle some of the painpoints that are within ember today. I’m specifically talking about onboarding new ember developers and second is to clarify the hassle with configuration files.

1 Clarify Configuration

Let’s start with ember’s configuration hassles. I start by describing the problems from my point of view, share my ideas to improve this situation and afterwards some fresh thoughts for the future’s future.

1.1 Problem Description

Let’s start with ember’s configuration hassles. I’m specifically talking about index.js, ember-cli-build.js, config/environment.js and app/app.js files. Let’s structure things here a bit and then dive in to make them more understandable. For a better understanding I related these files to their “code structure” (either class or model that is associated with that file), a use case in short words and the relevant scope in which they are executed.

FileCode StructureUse CaseScope
ember-cli-build.jsEmberAppConfigure the build of an appbuild
index.jsAddon ModelConfigure the build for an addon in the host project
build
config/environment.jsConfigures the runtime of a projectruntime
app/app.jsApplicationConfigures the app instanceruntime


Finding the Documentation

In order to understand how a config file works and what kind of properties and values can be configured, the first goto place would be to search for a documentation. Since there is not a good one present for config files, it is required to find the related code structure (I was desparately looking for a better name but wasn’t successful) and have a look at their source to understand it.
The table outlines that there are four different files with each having a different code structure associated with it (except config/environment.js). By opening a config file it is neither clear to which code structure that particular file relates to nor easily deferable. For example if you already aware that index.js refers to an addon, is the related code structure the Addon Model or the EmberAddon? A thorough understanding and inclusive mental model about ember-cli’s architecture is required. If this mental model is not present, there are multiple possibilites to search for the related code structure. The above matrix can be used to roughly estimate a probability to find the correct one – I guess it’s valid to say: “good luck!”

Config File Formats

Config formats are mostly JavaScript POJOs. They are either directly exported or wrapped into one of the code structures mentioned above and export that way. This differs from file to file, no clear convention either. That makes it utterly hard to find out which methods exists, which methods are used for which purpose and which are overridable, since there is also no code-completion for IDEs. At the moment, finding the right method to override for your use-case is playing hide and seek.

Addon Configurations

Configuring an addon is relying on the documentation of addon authors to learn about their configuration options. Most often they are provided but you have to grasp them from either readme files at github or their respective documentation site. You can be lucky, when all options are documentated thoughtfully, which isn’t the case most often. Sometimes addons evolve but documentation lacks behind. Second, when you are in your configuration object (inside a configuration file), you don’t even know, what addons you can configure (might even be a transitive addon you need to pass options to). Addon configurations don’t provide auto-completions either, it requires you to roundtrip the documentation pages.

Configuring Application Instance

By programmatically configure your application instance I address things like setting the locale of ember-intl or similar.
This, for sure and for obvious reasons, is been done in the application route’s beforeModel() hook… as you would expect (sorry for the sarcasm – I had to). It is a somewhat wrong place from an architectural point of view but probably resulted due to histortic reasons. Nevertheless, this is not the place for developers to expect that kind of setup routine.

1.2 Ideas for Improvements

I roughly outlined and described some of the painpoints that come with the way current ember applications are architectured. More important are ideas to improve this situation. My ideas try to find and establish conventions for the wild wild west of ember.

Convention for Project Layout

With Ember Octane, Module Unification (MU) is arriving, bringing a more opinionated and structured folder layout to projects and is possibly one of the most awaited feature in the ember world as of now. MU provides us with rules to follow a convention, unifies the look and feel for the source code for each project and a new developer can immediately feel home to start their work – which is absolutely great.

While MU mainly focus was the src/ folder and isn’t already finished we can start thinking about MU 2.0 which would aim at unifying the project layout. Given we are developing an ember addon it will soon be necessary to test this against classic and MU structured applications. Our beloved and hated dummy app isn’t capable to handle this. It will be required to have more than one app in your project. Luckily Chris Manson has done a proof-of-concept. It’s hard to guess what this repo is about (I was lucky, he showed me). This addon’s tests/ are hoisted into each application in packages/ and executed accordingly to test against classic and mu apps. Here an addon is showcased with in-repo apps located at packages/ (an app would have in-repo addons at this location). I would say it is fair to have in-repo projects there, regardless of what kind the host repo is.

Put in-repo projects into the packages/folder.

Convention for project layout.

Eventually it would also be good for addon authors to have a place for multiple applications: One classic app, one mu app … and what about a docs app? All of them can live inside packages/ and those application that the addon is tested against should signal that they are part of the test suite or not (e.g. a config flag).

Convention for Project Configuration File Locations

What I really struggled with, when beginning to work with ember addons were the index.js and ember-cli-build.js files. While apps use the ember-cli-build.js file to configure their build pipeline and addon has an index.js file with instructions how it affects the build pipeline of host applications. The weird fact addons contain both files puts more complexity on it and to be honest – how often we changed code in the ember-cli-build.js file but we meant to change it in index.js. The essence is:

One build pipeline file per project.

Convention for project configuration file locations (1).

It could be as easy as config/build.js for every project.

Most configuration files already have a good fit in the config/ folder – keep that as is, it’s already good. Though with a possible build.js present  it would be reasonable to rename environment.js to runtime.js to give a clear indication between build and runtime.

Put everything configuration into the config/ folder.

Convention for project configuration file locations (2).

Convention for Configuration File Format

This especially applies to the config/build.js file(s), which has no unified format as of yet. I’d rather see a base class that gets subclassed and rexported, something like this:

// build.js
const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

class MyCoolEmberAdoon extends EmberAddon {
	getConfig() {
		return {
			// my config here ...
		};
	}
}

The getConfig()here is schematic and can literally be anything to get your projects config. The more more important part is that whenever I place the cursor in the class body and press ctrl + space I would get autocompletion by my IDE which suggests me what methods I can override. Similarly when inside a method body, autocomplete helps me with methods I can access there. Very similar to how we write components, the file above should look very familiar.

Fix Application Setup Routine

The setup routine at the moment is in a somewhat wrong place (the ApplicationRoute’s beforeModel() hook). I would have expected it to be in the Application class or any dedicated class whose purpose is to setup my application:

// src/main.js
import Application from "@ember/application";
import Resolver from "./resolver";
import config from "../config/environment";

export default class MyApp extends Application {
	config = config;
	resolver = Resolver;

	setup() {
		// here
	}
}

Remarks on this code snippet: The class is given the configuration and as such has access to config.modulePrefix which it can use to import loadInitializers() on it’s own, as part of the Application.constructor() method. I don’t see any need to have this in my main.js (it is currently present in there).

Application has a dedicated location for their setup routine.

Convetion for application setup routine.

Since most of the setup code is programmatically configuring addons (e.g. ember-intl) it requires access to the glimmer-di container, which the setup()method in my example should have access to. I would prefer some code structure where I can use code I use in other ember classes, too (components, services, routes), e.g. use @service decorator 🙂

Convention for Addon Configurations

First of all, the configuration key for an addon inside config/build.js or config/runtime.js should be unified for better visual inspection. With ember-cli 3.5 (or 3.4?), it already raises deprecation warnings when the package and addon name diverge. Treating that same name as key inside your configuration hash would keep all of that in good synchronicity.
Second would be to force addon authors to provide a file describing their configuration format. Could be something like a JSON schema file under config/addon.json. These files can then be used to provide auto completion when inside the configuration hash through the ember language server.

Addons provide a schema file for their configuration.

Convention for addon configurations.

Additionally, addons like ember-cli-addon-docs can consume this file to automatically generate your addon configuration, taking this step off of the user to write documentation for that. Hence, ember-cli-addon-docs can provide these information in a similar fashion, it would no further been necessary to scan readme files or custom documentation, you can expect it documented at a certain location.

Summary and Example

The careful reader already has realized that I follow my own terminology already. I’m talking about ember projects instead of ember app or ember addon as soon there will be more, like engines or all the glimmer projects (see below, too). Those projects can even have subprojects located in packages/ regardless of what type the host and subprojects are. I realized it simplifies thinking about ember’s structure a lot. I’d encourage you to do the same.

This is how an ember addon project can look like:

config/
  build.js
  runtime.js
  addon.json
packages/
  classic/
    config/
      environment.js
    app/..
    addon/..
    ember-cli-build.js
  docs/
    config/
      addon-docs.js
      build.js
      deploy.js
      runtime.js
      targets.js
    src/..
  mu/
    config/..
    src/..
src/..
package.json

1.3 Presets, Blueprints and Ember CLI UI

With ember-cli-create I introduced presets to pre-configure an ember project. Mainly because I don’t want to create my own blueprints as I thought I would have need to keep them in sync with updates happening in ember itself – I have been told they may be extendable. This still requires investigation and research. Nevertheless I see presets as a lightweight way to provide a configuration for your ember project. The idea is mainly taken from vue-cli. The goal with ember-cli-create definitely is at some point to consume your personal or company created presets, e.g.:

$ ember-cli-create my-cool-project --preset gossi/my-preset

With conventions for addon configurations those can be used to parametrize presets and put their provided values into the configuration files of a generated project. Even more fine-grained with a given preset that prompts you for a configuration of a certain addon any tool can build some form of UI to ask for respective values.

At this point the boundaries of presets and blueprints are still unclear and won’t be discussed any further in this article – I’ll leave that to the community.

A potential ember-cli-ui (btw: we deserve a better name here, something heroic!) would greatly benefit from those conventions. First of all with ember-cli-create under the hood, it would provide a graphical UI for project generation. A clever architecture would help to share the same questions ember-cli-create wizard’s has at the moment with a GUI equivalent. Second, the GUI can list all installed ember-addons, inspect them one-by-one and craft a nice interface to configure each of them, without even the need to open a config file in your editor. Despite all the other cool stuff a GUI tool can do.

I do want to use such a tool as much as you would but I see the problems I outlined in this article to be solved as a prerequisite to start working on such a nice tool.

2 Onboarding Developers to Ember

Developers new to ember should get the information they need, to start their first ember project, right at their fingertips. Previously when a developer was about to start a new project, it must have been clear before the first ember command is typed, what kind of project it will be (app or addon) to run either ember new or ember addon. With glimmer in place the field of projects that will be supported by the ember-cli pipeline is increased and the mantra is changing to start simple and then installing more and more packages to “ember up your way”. This is handy and should be better supported by ember-cli itself. Here is a nice way I did for ember-cli-create:

Running the command without any arguments will provide you with a nice wizard that guides you through project creation. For beginners the most important line is “-> If you are new to ember, use this!”. They exactly receive the hint, they are looking for. All the other options are for developers that already are aware of ember and can pick the project they want. To politely embrace and jumpstart developers after project creation: the finish screen will tell the developer what their next steps are.

Another idea would be to integrate ember observer with ember install. Whenever the command is run without arguments can provide a search field, queries ember observer with the best 5 search results. Selecting an addon will install it.
@ember/optional-featureswould actually only require one cli command: ember features to list, activate and deactivate them. That change would immediately improve UX/DX quite effectively. The derieved pattern here is straight forward.

Whenever an ember command is run without any required arguments, an interactive mode is entered asking questions what to do.

Convention for ember-cli commands.

The idea stems from Symfony Console (php) API, which enables each command to use an interactive mode. Gladly enough the wonderful Kelly Selden has already adopted this pattern with ember-cli-update, which provides a wizard to select and run codemods after updating your ember project.

These are some examples regarding improvements for ember-cli, I guess there are plenty more. I can’t wait to be surprised by a creative, cleverly crafted solution that’ll help do my job more effectively 🙂

Beginner Blueprint

The Beginner preset I built into ember-cli-create is a nice first hint for new learners to start with ember. Currently it will install an ember app with the welcome-page installed. Serving this will show a huge tomster and some text (does anybody read that one so far?). I think this is far from ideal. Chris Garett described ember as a Component-Service Framework. The beginner blueprint should embrace this. Three things should be covered:

  1. Routes
  2. Components
  3. Services

Routes to explain defining routes and how to handle linking between them. Components as they are the key feature and services to share state between them. Each of them should come with proper jsdoc to and well explaining code comments. From reading code and comments it should be clear how ember works. Comments can tutor beginners by suggesting next steps, e.g. “uncomment the next block of code to enable a third route in your ember app”. Such an onboarding journey could also be realized by using the recently released GitHub Learning Lab.

Summary

I explained some of the painpoints I have when working with ember, e.g. when facing a bug that is seeing the light from the ember-cli abyss or trying to configure my ember projects but opening the wrong file, wondering for years why no change I do has an effect onto my build. I would say I only scratched the surface of what gives me a bad developer experience and ergonomics with ember. I hope my ideas to improve this situation will attract others to join my wonder- and colorful thoughts on nice work environments and we can share, discuss and build on real improvements that will benefit us all.

– gossi