You’re happily building your app creating and using computed properties, but you’ve noticed that after a while some things aren’t updating when they should be. What gives?

What you’ve uncovered is the dependencies you declare upfront aren’t tied to the computed property body in any way. It is up to you to make sure they stay in sync with one another. There are no guard rails. Ember will happily chug along with your computed property accessing undeclared dependencies.

Here’s a simple example to demonstrate the issue:

The above code is fairly straightforward. There’s not much to make it go wrong, right? Let’s see just how easily we can introduce a bug with a seemingly innocent change. Imagine we just received a user story saying that “As a user, I want my full name to display my middle initial too.”

During code review, it’s easy to skip over a diff like this (and add a simple 👍). If the computed property’s body is large enough, the reviewer could miss that the change is inside a computed property, let alone if the change introduced a new dependency.
Is there any hope in sight? Any tooling help? Unfortunately, no. All we have at the moment is diligence.

Exploring a computed solution

One Investment Friday we thought it would be fun to explore what we could do protect our codebase from human error. We came up with something we called computed strict.

If you want to play with computed-strict check out the above example with this ember-twiddle

The first thing you’ll notice is that not a whole lot has changed. The general shape of a computed property stays the same. The only visible changes to the developer is now instead of calling this.get('propName') call propName(). Once you wrap your head around that there really isn’t anything new to do.

Our main goal with computed strict was to prevent calls out to properties that weren’t listed in the dependent keys. We found inspiration from previous work with AngularJS and its use of dependency injection.

To enforce strict dependency compliance we bound the function (using apply) to a Proxy object that would warn if a call to this.get was attempted (and prevent it from happening). To allow access to the dependencies we mapped each dependent key to a getter function and passed them in to the computed property function as arguments.

At the time we authored computed strict, Proxy wasn’t well supported in browsers. But now at the time of writing, Proxy is almost fully supported with IE being the last holdout. Until IE supports Proxy it is risky to rely on it for a production web app.

Mitigating the risk of mis-matched dependencies

Until Ember adds a way to automatically track dependencies the best safeguard against dependency mis-matches is a simple, completely non-technical solution. It’s time to update the checklist in your pull request template.

Here’s a sample snippet that at least puts the question about dependency mis-matches in the mind of the changeset author/reviewer:

## Reviewer Tasks:

- [ ] This PR contains modifications to an ember computed property
    - [ ] The computed property's body only contains calls to properties passed as dependent keys
    - [ ] There are no extra dependent keys that are not in the function body

Looking to the future

As we saw above it’s easy to create dependency mis-match errors because change tracking is entirely manual, and they are so easy to overlook during review. The PR template isn’t the most optimal solution, but it’s what we’ve got to work with today.

I’m hopeful that one day in the not too distant future this will be solved in Ember. Whether it’s a core Ember feature, some kind of static analysis tool we can use during development, a cross-browser supported library we can include during run-time, or something else entirely. Who knows, it’s the future we’re talking about and anything can happen, right?

technology logo

Get a Free Consultation

Your Free Consultation will be packed full of discussions, brainstorming, and hopefully, excitement. The meeting is designed to help uncover your challenges, define your needs, and outline possible solutions so you can make decisions that will lead to the business outcomes you desire.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.