Ep. 33 - Code dependencies are the devil

Published: June 4, 2018, 9 a.m.

Have you built your app on someone else's code? And beyond that, does the "secret sauce" of your product depend on external libraries or frameworks? While it's tempting to use the latest and greatest tech as soon as it comes out, that's not always a great idea. In this episode, Bill explains why, and what to do to protect your code.

Written by Bill Sourour: https://twitter.com/BillSourour

Read by Abbey Rennemeyer:\xa0https://twitter.com/abbeyrenn

Original article:\xa0https://fcc.im/2HerXO5

Learn to code for free at:\xa0https://www.freecodecamp.org

Intro music by Vangough:\xa0https://fcc.im/2APOG02

Transcript:

\u201cChange is the only constant\u2026\u201d \u2013 Heraclitus (Philosopher)

The tools, libraries, and frameworks we use to build our web applications today are drastically different from the ones we used just a few short years ago.

In a few short years from now, most of these technologies will have changed dramatically again. Yet, many of us make these a central, inextricable part of our apps.

We import, use, and inherit from the flavor-of-the-month frameworks as if they\u2019re all going to be around and unchanged forever. Well\u2026 they\u2019re not. And that\u2019s a problem.

After 20+ years of developing, designing, and architecting web applications, I\u2019ve come to appreciate two important truths:

External dependencies pose a great threat to the long term stability and viability of any application.

It\u2019s increasingly difficult\u200a\u2014\u200aif not impossible\u200a\u2014\u200ato build any kind of non-trivial app without leveraging external dependencies.

This article is about reconciling these two truths so that our apps have the greatest chance of long-term survival.

The rabbit hole is very deep indeed.

If we start thinking of all the things our web apps depend upon it\u2019s easy to think of a dozen or more before we even get to the code:

Power
Connectivity
Firewall
DNS
Server Hardware (CPU, Disk, Ram, \u2026)
Cooling
Virtualization Platform
Container Platform
Operating System
Web Server Platform
App Server Platform
Web Browser

As developers, it\u2019s good to be aware of these things, but there\u2019s often not much we can do about them. So, let\u2019s ignore them for now and talk only about the code.

In code, there are three kinds of dependencies:

1. Dependencies we control
This is code written and owned by us or our organization.

2. Dependencies we don\u2019t control
This is code written by a third party vendor or open-source software community.

3. Dependencies once removed
These are the code dependencies our third-party code dependencies depend upon. (Say that three times fast!)

We\u2019re going to talk mainly about dependencies we don\u2019t control.

Dependencies we control and dependencies once removed can still cause headaches, but in the case of dependencies we control, we should be able to directly intervene and mitigate any problems.

In the case of dependencies once removed, we can usually rely on a third-party to take care of it for us, since they are dependent on these, too.

Why third-party code dependencies are good

A large portion of your web application exists to solve common problems: authentication, authorization, data access, error handling, navigation, logging, encryption, displaying a list of items, validating form inputs, and so on...

Regardless of which technology stack you use, there\u2019s a good chance that common solutions to these problems exist, and are available as libraries that you can easily acquire and plug-in to your codebase. Writing any of this stuff completely from scratch is generally a waste of time.

You want to concentrate on code that either solves an uncommon problem or solves a common problem in an uncommon way. That\u2019s what makes your application valuable: the code that implements the business rules that are unique to your app alone\u200a\u2014\u200athe \u201csecret sauce.\u201d

Google\u2019s search and page ranking algorithm, Facebook\u2019s timeline filtering, Netflix\u2019s \u201crecommended for you\u201d section and data compression algorithms\u2014 the code behind all of these features is \u201csecret sauce.\u201d

Third-party code\u200a\u2014\u200ain the form of libraries\u200a\u2014\u200aallows you to quickly implement those commoditized features of your app, so you can stay focused on your \u201csecret sauce.\u201d

Why third-party code dependencies are bad

Take a look at any non-trivial web-app built in the last couple of years and you\u2019ll be absolutely astounded by the amount of code that actually comes from a third-party library. What if one or more of those third-party libraries changes drastically, or disappears, or breaks?

If it\u2019s open-source, perhaps you can fix it yourself. But how well do you understand all the code in that library you don\u2019t own? A big reason why you use a library in the first place is to get the benefits of the code without having to worry about all the details. But now you\u2019re stuck. You\u2019ve completely tied your fortune to these dependencies that you don\u2019t own and don\u2019t control.

Perhaps you think I\u2019m exaggerating, or speaking from a purely academic point of view. Let me assure you\u200a\u2014\u200aI have dozens of examples of clients who completely snookered themselves by embedding third-party code too tightly into their app. Here\u2018s just one recent example\u2026

A former client of mine built their app using a Backend-as-a-Service provider owned by Facebook, called Parse. They used a JavaScript client library provided by Parse to consume the Parse service. In the process, they tightly coupled all of their code\u200a\u2014\u200aincluding the \u201csecret sauce\u201d code\u200a\u2014\u200ato this library.

Three months after my client\u2019s initial product launch\u200a\u2014\u200ajust as they started getting some good traction with real, paying customers\u200a\u2014\u200aParse announced it was shutting down.

Now instead of focusing on iterating on their product and growing their customer base, my client had to figure out how to either migrate to a self-hosted, open-source version of Parse, or replace Parse completely.

The disruption this caused for a young, fledgling application was so huge that my client eventually scrapped the app entirely.

Balancing the good and the bad

Several years ago, my go-to solution for overcoming the risks while retaining the benefits of third-party-libraries was to wrap them using the Adapter Pattern.

Essentially, you wrap the third party code in an adapter class or module that you\u2019ve written. This then works to expose the functions of the third party libraries in a manner that you control.

Using this pattern, if a third-party library or framework changes, or goes away, you only have to fix a bit of adapter code. The rest of your app stays intact.

This sounds good on paper. When you have self-contained dependencies that only provide a few functions, this will do the trick. But things can get ugly fast.

Can you imagine having to wrap the entire React library (including JSX) before using any of it? How about wrapping jQuery, or Angular, or the Spring framework in Java? This quickly becomes a nightmare.

These days I recommend a more nuanced approach\u2026

For each dependency you want to add to your codebase, evaluate the level of risk it will introduce by multiplying two factors:

The likelihood that the dependency will change in a material way.

The amount of damage a material change to the dependency would do to your application.

A third-party library or framework is less likely to change when some or all of the following things are true:

It has been around for several years and has had several major releases.

It is widely used by many commercial applications.

It has the active support of a large organization\u200a\u2014\u200apreferably a household name company or institution.

A third-party library or framework will do less damage to your application when some or all of the following things are true:

It\u2019s only used by a small portion of your application, rather than being used throughout.

The code that depends upon it is not part of that \u201csecret sauce\u201d I talked about earlier.

Removing it requires minimal changes to your codebase.

Your entire application is very small and can be rewritten quickly. (Be careful with this one\u200a\u2014\u200ait\u2019s rarely true for very long.)
The riskier something is, the more likely you should be to wrap it or avoid it altogether.

When it comes to the code that is really central to the value proposition of your application \u2014your \u201csecret sauce\u201d\u200a\u2014\u200ayou need to be extremely protective of it. Make that code as independent as possible. If you absolutely need to use a dependency, consider injecting it rather than directly referencing it. Even then, be careful.

Sometimes this means saying \u201cno\u201d to a third-party library you think is really cool, or that you really want to use for one reason or another. Be strong. Trust me, it will pay off. Just ask all those people who invested heavily in the very first release of Angular, or my former client who used Parse everywhere. It\u2019s no fun. Believe me.

Speaking of fun, take a look at this\u2026

The image above is the dependency graph for an application called TinyTag Explorer.

Generating a dependency graph for your existing apps is a great way to understand the level of risk being introduced by your dependencies. I\u2019ve put together a list of free tools for generating graphs similar to the above in a variety of languages including JavaScript, C#, Java, PHP, and Python. You can get it here.

Help me help others

I want to help as many developers as I can by sharing my knowledge and experience with them. Please help me by clicking the \u2764 recommend button (green heart) below.

Finally, don\u2019t forget to grab your list of free dependency graph generators here (link in original article).