When I began working as the first dedicated frontend engineer at Scripted in early 2014, AngularJS was taking over the world. During my interview, one of my soon-to-be co-workers even asked me to help him refactor his side project in Angular. We live-coded it together on a projector in front of the whole engineering team.

At the time, the decision to choose Angular for our frontend seemed obvious. It was not without its trade-offs, but the known shortcomings were not issues for our use case. I liked the opinions it enforced around application architecture and loved its declarative templating system with composable directives.

A little more than two years later, the industry at large has decided that our ‘obvious decision’ was actually a horrible mistake. The framework’s own authors admitted as much when they announced that Angular 2 would be a complete rewrite.

Be that as it may, we’re not switching anytime soon. To do my job well, here’s what I need from a framework:

  1. Abstractions should be easy to create.
  2. Abstractions should be easy to use.
  3. Abstractions should be easy to refactor.

I repeat “abstractions should be easy” three times, because it’s important. Everyone’s already sold on abstractions (components, services, whatever you want to call them — they’re winning). Easy is the key word for me.

If I start working on a new feature, and halfway through, I notice that a few of the functions should be abstracted into a service, my first instinct is “I’ll do it later.” I can fight that instinct, but it goes better if the lazy part of my brain has no ammo. If I have to write a bunch of boilerplate, add a dependency somewhere, think of where I should put the new file and create a new spec file with its own boilerplate, I’m in for a tough battle.

The key advantage of an opinionated framework is that it cuts down on the daily decision count. Angular almost accomplishes that, but it misses the mark in two critical ways:

  1. Browsers have no concept of folder structure, so its opinions are at best suggestions. That gap can be bridged with a build system that supports and enforces your architecture.
  2. Too many opinions. These days at Scripted, the only building blocks we use are services, directives and filters.

Before my tenure, Scripted was a Rails monolith. My first task was to move the frontend into its own repository. This meant that I had the opportunity to write my ideal build system.

Here’s our structure:

  • apps (each app is served from scripted.com/:app*)
    • admin
    • business
      • directive
        • homeBox.js
      • service
      • filter
    • documents
  • components (same as apps structure but built first so they can be required by any of the apps)

Which lets our files look like this:

function homeBox(){
  return {
    transclude: true,
    scope: {title: '=homeBox'},
    template:
    `
    <div class = "home-box">
      <h3 class = "title" ng-bind = "title"></h3>
      <div class = "inner" ng-transclude></div>
    </div>
    `
  }
}

No boilerplate, and the file is automatically included in its app as a directive named homeBox, because of its folder name and location.

I’m sure Angular 2 and the latest React ecosystem of choice are great frameworks, and I like what I’ve seen in my limited dabbling. At work, I need to be efficient with my decision-making energy, and our current system feels like a magic wand that gets more powerful every few weeks. Switching to something new would be a massive short-term decision drain, and I remain unconvinced it would be worth the loss.

I’d be happy to write a follow-up post about how some of our build systems work, maybe discuss auto-generated unit tests if people are interested. Given Angular 1’s popularity on Hacker News lately, though, I’m not holding my breath.