Naming and API Design

June 15, 2015

A tenet of design is that things that are the same should be obviously the same, and things that are different should be obviously different. This is the most basic way to create consonance and contrast, and is a great rule of thumb when designing anything. While Rails diverges from this in an annoying way1, Angular diverges in a baffling way that makes explaining it to a newcomer head-scratchingly difficult.

The World’s Silliest Programming Language

Imagine the following programming language. To define a function, we name it using a dasherized scheme, with square brackets to offset the arguments:

define full-name[first-name,last-name]
  first-name + " " + last-name
end-define

Further imaging that in this programming language, to invoke the function you use a camel-cased version of the function name, and use parenthesis to offset the arguments:

print fullName("Dave","Copeland")

I don’t think it’s a stretch to call this language poorly designed. Things that are the same—a function—are handled differently depending upon the occasion. The design of this language would benefit greatly if you defined functions using the name symbol used to invoke them.

This example might seem pretty ridiculous, but this is exactly what Angular does.

Dashes, Camels, and “ng”

Angular uses directives in HTML to bind your code to the DOM or to DOM events. The simplist one to understand is ng-click:

<button ng-click="doit()">Do It! Do It, Now!</button>

This calls doit, whenever the button is clicked.

Are you curious about the documentation for ng-click? It’s filed under ngClick. The text ng-click—the thing you must type into your application’s source code to make it work—does not appear in Angular’s source code. Everything talks about ngClick. Why? I have no idea.

It gets worse.

Good Luck Finding That Thing You Downloaded

I’m writing a book on getting Angular, Rails, Bootstrap, and Postgres working together, and how these four technologies in sum are greater than their parts. It’s going to be awesome. Documenting Postgres is easy (“check out this thing you didn’t think a relational database could do!”), and for Bootstrap, it’s just as simple (“put this class on this element and…boom!”).

Angular has resulted in many passages like the following. I’m talking about Angular’s router, which is a separate component. The first thing the user has to do, after downloading the module, is configure it in their application. Essentially, you need to say “my app requires the router I just downloaded”.

That argument [when declaring the app] is our app’s list of dependent modules. It’s currently empty, because we hadn’t needed anything other than what’s provided by Angular. Now, we’ll need to add angular-route to this array.

Unfortunately, it’s not as simple as adding "angular-route" to the array. In Angular, the module name for declaring dependencies doesn’t have to be the same as the name of the module we downloaded. For official Angular-provided modules this is unfortunately the case.

By convention, the name to use in code for an Angular module can be derived by replacing the angular- with ng and camel-casing the remaining module name. That means that angular-route becomes ngRoute and so "ngRoute" is the string to add to our list of dependencies.

The reader has had type one string—“angular-route”—into their application’s source code already (so that Bower/NPM/Whatever.JS could download it) And now, I have to give the reader an algorithm they must execute mentally in order to know what string to type into another part of their application.

This is bad design.

When faced with bad design, there is often some sort of tradeoff, some reason the system was designed this way. While I can hazard a few guesses about the whole ng-click/ncClick issues2, I’ve got zero clue why the name of a module in your dependencies shouldn’t be the name of the module you downloaded.

It’s so confusing that I have to invent a new phrase just to explain the difference. If I download angular-route and use ngRoute in my code, which of those is the module name? Who knows?

Imagine if the module name was the…well…module name?

That argument [when declaring the app] is our app’s list of dependent modules. It’s currently empty, because we hadn’t needed anything other than what’s provided by Angular. Now, we’ll need to add angular-route to this array.

End of description!

Same things should be same.