10 considerations when developing Ember CLI addons

Why these 10 things?

Through the authoring of 10 Ember CLI addons, and contributions to several more, I have compiled a list of considerations to keep in mind while developing your own.

#1 - Do for your users programatically what must be done for things to function correctly, and document those that cannot.

UPDATE (20 Feb 2016): After having a conversation with Luke Melia about this consideration I believe that I misled myself about a few things due to an improperly configured environment and possibly also a misunderstanding of some concepts. The recommendation to do things programmatically for your users is still a good one, though the example I had given was not as much of one.

As such I have removed it in order to not be mis-leading and if I can come up with another one I will add it.

#2 - The setting of native configuration options in the application should also apply to your addon, because why would the user expect anything different?

Ember CLI provides a variety of options that can be configured to affect the compilation of assets in an application. Most of the time the code provided by your addon will be able to align with these features without any additional effort on your behalf. Sometimes though your addon may be constructed in such a way that you need to take additional steps to ensure support. This goes along with the first consideration, where a user may have read the Ember CLI documentation and expects that these features will "just work"™ and without you having made sure they do will not realize that they are not.

An example of such steps that might be taken can be found in the sl-ember-components addon. This addon provides Ember-compatible Twitter Bootstrap functionality, including support for Glyphicons. These font files are included in the distributed source code of Twitter Bootstrap and must be exported out of the addon and into the consuming application. Since these files don't exist on disk within the addon's codebase the ability for them to be fingerprinted is not automatic and steps must be taken to integrate them. This is because a developer of an Ember application expects to be able to configure whether fingeprinting is enabled or not and for either the default settings to be used or to be able to specify which extensions to enable it for.

As you can see in the index.js file the decision was made (in included()) to add the extensions of eot, svg, ttf, woff, and woff2 to the default ones supported by Ember CLI in the case where a user enabled fingerprinting in their application but did not specify specific extensions to do so for, and to also respect any specific extensions the user lists.

#3 - Good architecture makes for friendly code

It is helpful to have real-world examples when discussing these considerations so I am going to refer to the broccoli-asset-rev addon for this next one. My intention in doing so is not to be critical, but illustrative. In fact, Rick Harrison was very receptive to our suggested changes we submitted a PR for, which I will describe below.

You have built your Ember CLI addon for others to use in their applications but you cannot know all of the ways that they may need to use it. Obviously if you don't know all the ways they may need to use it you cannot know all the things your code needs to do but there are ways you can structure it to be as helpful as possible. While developing the fingerprint support for fonts described in the previous consideration we needed to know the default values set by the broccoli-asset-rev addon but there was no way to get them. They were defined in-line in a property that was never exported:

// index.js

module.exports = {
    initializeOptions: function() {
        var defaultOptions = {
            enabled: this.app.env === 'production',
            exclude: [],
            extensions: [ 'js', 'css', 'png', 'jpg', 'gif', 'map' ],
            prepend: '',
            replaceExtensions: [ 'html', 'css', 'js' ]
        };

        ...
    };
}

This meant that we would have to hard-code these values into our addon, introducing a brittle association because we would not remain in sync with any changes made to these values in the broccoli-asset-rev code.

Had these values instead been defined in a separate property that was exported then not only could they be used within the broccoli-asset-rev codebase but they could also be used by any other codebase needing them. We submitted a PR introducing such a change, which now looks like this:

// lib/default-options.js

module.exports = {
    assetMapPath: '',
    exclude: [],
    extensions: [ 'js', 'css', 'png', 'jpg', 'gif', 'map' ],
    fingerprintAssetMap: false,
    prepend: '',
    railsManifestPath: '',
    replaceExtensions: [ 'html', 'css', 'js' ]
};


// index.js

var defaults = require( './lib/default-options' );

module.exports = {
    initializeOptions: function() {
        var defaultOptions = {
            enabled: this.app.env === 'production',
            exclude: defaults.exclude,
            extensions: defaults.extensions,
            prepend: defaults.prepend,
            replaceExtensions: defaults.replaceExtensions
        };

        ...
    };
}

so that now in the sl-ember-components addon we can also import the default option values and use them in included():

var fingerprintDefaults = require( 'broccoli-asset-rev/lib/default-options' );

#4 - Do for your users programatically what you can to minimize the work they must do

Remember the previous consideration of doing for your users programatically what must be done for things to function correctly? You should also do this anytime you can to help your users out. The ember-cli-jsdoc addon adds JSDoc support into Ember CLI addons. In order for it to function correctly a jsdoc.json configuration file needs to be created. You could choose to approach the creation of this file a few different ways. You could provide instructions to the user on how to populate this file. You could even provide a sample file for them to start with. Or you could choose to leverage the power of code to assist your user as much as possible. The blueprint for this addon does exactly that. Since Ember CLI applications have different folder structures than Ember CLI addons different configuration options are needed. The blueprint is able to determine which folder structure the addon is being installed into and create a configuration file specific the user's needs.

#5 - Make your addon configurable

The sl-ember-components addon applies the sl-ember-components CSS namespace to each component so that they can be specifically targeted for styling. But what if a user needs this value to be something different? Via a configuration option a user can specify their own CSS namespace value to use. The config() and treeForVendor() methods in the index.js file provide support for this feature as well as the .@{component-class-prefix}-componentName syntax in each component's LESS definitions. Taking these few extra steps provides complete flexibility in the usage of the addon for the user.

#6 - Don't impose unneccesary constraints on your users

This is again best explained by a real example. One of the deficiencies of the sl-ember-components addon had been that it required a user to install the ember-cli-less addon in order to use it. This had the potential to conflict with any applications using SASS, not to mention having to install LESS even when not using or wanting to use a preprocessor. This was in bad form but has been resolved in the v0.11.0 release by compiling the addon's LESS source code in treeForVendor() in index.js so that it gets built into the vendor.css file.

#7 - Give some thought to your error-handling strategy

There may be times in your addon's code that you want to write information to the console or to throw an error in order to assist users in its usage. While this is not a bad idea, how you implement it may be. It is very likely that your code will ultimately be part of a production application exposed to users and console logs, console errors, and the throwing of errors may not be a desired paradigm in the deployed application. Requiring your users to strip out these calls by using something like the ember-cli-defeatureify addon (read how to do so here) goes against the previous consideration of not imposing unncessary constraints on your users. You could strip these out for your users automatically during the build process, which is also another previous consideration.

Another consideration is assisting your users in knowing where the errors are coming from and to be able to selectively respond to them. In the sl-ember-components addon instead of just throwing a native error each error thrown is of a specific type. This way the Ember.onerror method can be defined in a consuming application and each error type can be specifically targeted. In this case, there is an error type for each component that throws errors.

#8 - Keep the size and number of required dependencies to a minimum

There are two types of dependencies your addon can require. Those that it needs for the development of the addon itself and those that need to be built into a consuming application. The ones you need to keep an eye on are the latter. Evaluate your reasons for needing these dependencies and see if you can't recreate some of the functionality yourself in the addon. Is it really necessary to require an entire 57Kb library just because you need one method out of it? Not only does this cut down on the size of the built application but it will also reduce the amount of potential conflicts that may occur with other dependencies installed by other addons or the consuming application.

#9 - Continue to support previous versions of Ember or other dependencies

This consideration isn't really applicable when you first develop an addon but as new versions of your addon's dependencies are released and you keep your addon up-to-date with them you should consider supporting previous versions for as long as it makes sense to (for you and your users). Just because you need to upgrade the dependency in order to take advantage of its new capabilities it doesn't mean that your users are ready to make changes to their application to accommodate the new ways your addon needs to be used. Yes, they could choose not to upgrade your addon, but spend enough time developing, keeping dependencies up to date, and releasing new versions of your addon and you will run into the overlaps of these needs where you need to support multiple versions of a dependency within an addon. When this need arises you can use a tool like the ember-cli-version-checker addon to do so much more easily.

#10 - Keep FastBoot compatibility in mind

I'll admit that I am not versed well enough on the particulars of FastBoot to make any specific recommendations but I do know that it will be an important part of the Ember ecosystem and that users will want Ember CLI addons to be compatible with it. So you should definitely remain aware of its development and integrate compatibility when it makes sense to do so.

Have your own considerations?

I hope that this information has given you some ideas to keep in mind while developing your addons. Do you have your own considerations you think others should know about? Recommend them in the comment section below!

Show Comments