JavaScript, Node.js, and the Flying Wedge Anti-Pattern

Oct 29 2011

The "Flying Wedge" is the name of a military formation in which troops would march into battle in a V-shaped formation. That same V-shaped "formation" unfortunately has a tendency to show itself in JavaScript and CoffeScript. And in the vast majority of cases, it is bad practice.

Code that's easy to read, modify, re-use, and debug is better code. A convention that hampers those goals without offering an overriding benefit is an anti-pattern. That, in a nutshell, is why the flying wedge ought to be avoided.

Here's an explanation of the anti-pattern, along with some suggestions for avoiding it. <!--break-->

What is the Flying Wedge

The flying wedge occurs when blocks (usually of anonymous functions and control structures) become nested in a deep V-shaped pattern.

Here's an example, derived from a Node.js package I found "in the wild":

function myContainerFunction(data) {
  this.outerFunction(function (data) {
    data.innerFunction(function (data) {
      data.anotherInnerFunction(function (data) {
        data.yetAnotherInnerFunction(function (data) {
          // And so on...

In the coding sample this was modeled after, the nesting depth was 10 at its deepest. That script ended like this:


While not the only way a flying wedge can end, the above exemplifies one of the most common patterns -- a long series of closing anonymous functions.

The flying wedge is common in JavaScript because of the ease with which bits of functionality can be grouped into anonymous functions (or closures). And Node.js's callback-and-event driven model sometimes makes it appear as if the flying wedge were the right way of doing things.

But it is not. The flying wedge is an anti-pattern -- a pattern that introduces more problems than it solves.

Why is the Flying Wedge an Anti-Pattern?

Before answering the question, let's ask the opposite: Why do developers create flying wedges in their code?

  1. It seems to be concise. Instead of writing a bunch of named functions, closures are created as "throw away" functions.
  2. It makes use of clever scoping of variables.
  3. It works well in Node's callback- or event-based model.

Those are the reasons I have heard offered in favor of the wedge. Of the three, the second and third are both based on misunderstandings. In almost all cases, the same code can be accomplished by naming and defining the function elsewhere, and calling it by name.

But the objection isn't that there is another way of doing things. The objection to the flying wedge is that it introduces more problems than it solves. Here are some of the problems it introduces:

  1. The code is notoriously difficult to read.
    • It becomes difficult to track which scope a variable is in.
    • The code has to be read diagonally, and sometimes requires horizontal and vertical scrolling.
    • Visually, it is hard to keep track, as you read down the wedge, of which "layer" of the wedge you are in.
  2. Because of the difficulty scanning, it is easy to cause syntax errors -- or, worse -- syntax mistakes that do not generate errors, but cause bugs.
  3. The code is also difficult to modify. Just take a look at the closing section above -- ten lines of });. Now imagine a case where you needed to add a call at the end of, say, the fourth anonymous callback. Which closing paren is the right one? Counting on your fingers is not clever.
  4. Anonymous functions and closures are notoriously difficult to debug. The stack trace does not have a recognizable name for them.
  5. The code cannot be re-used. All those anonymous functions can really only be used in place. And when code can't be re-used, other bad habits (copy-and-paste coding, anyone?) creep in.
  6. IDEs and editors do not have a good way of abstracting and presenting these nested functions, and thus the tools you use become less effective.
  7. Inline documentation (doc blocks) are ineffective. Most languages, including JavaScript, now provide commenting conventions for extracting automated documentation. JSDoc, YUIDoc, and Dox are examples. The Flying Wedge makes it difficult or impossible to write documentation for these tools (particularly because the functions have no names).
  8. The Flying Wedge contravenes several other broadly accepted principles and patterns:
    • Nesting Depth increases Cyclomatic Complexity
    • LOC-per-function should be around 15 (or 30)
    • Re-usability is the primary purpose of a function

The flying wedge may look clever, and it may even seem at first to "get the job done quicker". But it will introduce more headaches than it is worth. Future-you will be angry with you, as will anyone else charged with maintaing or reading your code.