Delegate Wisely

On March 22, 2016, Azer Koculu in protest of losing a trademark dispute removed all his open source contributions including a package called left-pad from the Node Package Manager (NPM) repository. His action caused massive disruption to programmers and workflows around the world. The mere 11 lines of code were (and still are) a dependency on many popular tools and frameworks including React and Babel. It took several hours to recover, including a controversial move by the NPM corporation to re-publish and take ownership of the ‘pad-left’ package. That a seemingly inconsequential dependency could have such a significant impact demonstrates the power and peril of abstractions.

Abstractions increase our productivity by compartmentalizing complexity and reducing cognitive load. Abstractions come at a cost that is paid when they fail. Much like any number of disaster movies when “the system” fails, chaos follows, and people divide into two groups: the lost and the leaders—lost because they do not understand the reason and principles behind the system and the leaders because they both understand and can build anew. Abstractions are no different, if you fail to understand the principles and methods used to create them, you are doomed to become lost when they fail. You are effectively delegating both the work and the knowledge to a third party.

The right resolution

Knowing when you can delegate the burden of knowledge is an art. You don’t need to understand assembly to be a software engineer or know how electricity works to charge your smartphone, but perhaps you should know what assembly is and how electricity gets from the power lines to your wall outlet.

As a rule of thumb, you should know more about the nearest levels of abstractions than the further ones. For example, knowing how to turn off the electricity to an outlet or your whole house is important for your life and safety. While knowing which powerlines feed your neighborhood or which relay station powers your county becomes less important. As the abstractions move farther away your knowledge will typically be based on principles and concepts than concrete details.

In the context of technology when you take a dependency, whether that be a library, a service, or a platform, understanding how things work and how they can break will help you prepare for and mitigate disruption when (not if) they fail.

Managing delegation

Each time you depend on an abstraction you are accepting additional risk. Mitigating that risk results in logical branching and increased complexity (noise). Rather than focusing on accomplishing your use case, your code is riddled with mitigation strategies. At the most basic level, you must consider both the likelihood of failure and your tolerance for it. You might find yourself asking what could still work when the abstraction fails, how catastrophic will it be and how gracefully can you degrade. While many abstractions might offer a guarantee of performance, support, or availability not all do.

Whenever possible you should avoid taking a dependency. That includes doing things that may seem clunky or redundant. Consider whether the convenience or aesthetic is worth the additional risk of failure and requisite mitigation. Sometimes redundancy is the lesser evil.

Dependency testing

There is a rhyming Russian proverb that translates to, “Trust but verify.”

When taking a dependency is the right call consider that the contract between you and the abstraction is intended to be concrete and changes well communicated. However, sometimes the electric company reads the meter wrong. Sometimes they update their terms and conditions and you don’t take the time to read the fine print. Either way, things stop meeting expectations.

To mitigate these deviations consider implementing dependency tests. When you take a new dependency assert your understanding of the contract and how they fulfill your particular requirements. Doing so will allow you to discover side effects and verify that the abstraction is continuing to provide the intended value.

When to delegate

When evaluating an abstraction consider the motivation and focus of the owner. If an owner is providing functionality side-of-desk, as a proof of concept, etc. be judicious. These abstractions will likely be abandoned, broken, or could evolve in ways that become less useful. When the owner provides the functionality as a professional service, as a team mission, etc. you can have more confidence in its longevity, usefulness, and care.

Be cautious when delegating within the boundaries of your team or domain. These delegations or artificial boundaries tend to create noise for little to no return. Ask yourself:

Does the value of this delegation outweigh the additional complexity? Does this delegation require someone to go to multiple places to understand something otherwise holistic? Does this delegation need to occur now or in some potential future? Does this delegation solve a problem or is it simply a logical division? If logical how can we make that logical division as light-weight as possible?

When designing your software, repositories, systems, and libraries take a moment to realize that a pattern you may be very comfortable with might be unnecessarily noisy and could be simplified.