Thoughtful Dependency Adoption

Thoughtful Dependency Adoption

Summary

In a previous post, I wrote about the costs of bringing in a 3rd party dependency. One of the main points was to suggest that we avoid bringing in dependencies. However, this isn’t always possible. There are valid reasons to bring one in like to take a shortcut now to help deliver value to your company/product sooner (e.g., at a startup). This post picks up where that previous post left off and offers a method for how to approach bringing in a dependency, if you have to.

Note: although examples below are iOS biased, the ideas are platform agnostic.


Still want that dependency?

With the previous post in mind, let’s say we still want to bring a 3rd party dependency into our project. In some cases, it makes sense to add a dependency. Perhaps you are a startup trying to get funding and you need a backend quickly, maybe to support integrating the really complex subject of in-app purchases, or maybe you are just trying to prototype something quickly. Whatever the reason, here are some tips that I have found useful when bringing in a 3rd party dependency.

1. Justify the need

First thing to question, even for existing dependencies, is if you actually need the dependency at all. Maybe there is already a 1st party library available that you missed? If not, try and write the dependency yourself. This seems obvious, but we (including myself) are eager to skip this step and jump right for a new dependency. Even if it takes a day or two to write the functionality you need, you will better understand that part of the code and be better positioned to support that code in the future. Also, this is a great opportunity to learn something new!

For example, laying out UIKit user interfaces (UIs) can be tricky. To help with this libraries like SnapKit have popped up to make this easier. However, these days the native Auto Layout APIs have come a long way, which diminishes the need of something like SnapKit, but we sometimes blindly throw a dependency like this in since we’ve seen it used in the past or because someone, *cough* StackOverflow, tells us this is the route to go. Let’s say the current Auto Layout APIs aren’t as ergonomic to use as you would have liked. Check out this awesome post by Chris Eidhof - A Micro Auto Layout DSL. In something like 20 lines of code (LOC), you have a micro DSL that makes Auto Layout a joy. I use this in nearly all of my UIKit projects — no dependency!

If you attempt to write the functionality yourself, and you fall short. Well, you have just better justified the need for the dependency 💪.

2. Copy and paste

So, you tried looking for a 1st party library, tried writing it yourself, and that dependency is looking like a good option. You aren’t totally out of alternatives just yet. Try going to the 3rd party library and take a look at the functionality that you want to use (assuming it is open source). Chances are that you only want to use a small portion of the library. In that case, or if the dependency is small enough, consider copying and pasting the code into your project!

You may be saving your code base many LOC, and this is important because you are limiting what you have to support and the surface area of bugs. Also, you don’t have to bring in the complexity of managing that dependency, you can inspect the code further to better understand what it is doing, and you are more formally stating that you will support this code. You had to support it regardless even if you brought in as a dependency. We often think of dependencies brought in with a dependency management system (e.g., Swift Package Manager) as something external, and we therefore subconsciously discredit the need to support a dependency as if it were code that we wrote. Remember, if something goes wrong in that 3rd party code, your app at the end of the day is the one with an issue.

It is worth nothing that by copying code in from an open source project doesn’t mean you are abandoning open source. There is no reason why you can’t still contribute back to that code base if you find a bug or make a useful change to that code. This simply means that you are reducing the complexity in managing that dependency and potentially reducing the surface area of things that can go wrong when bringing in foreign code.

3. Isolate the dependency

The term spaghetti code comes up often to describe code bases with many systems and ideas intertwined and dependent on each other. Nobody starts out with a new project thinking they are going to write spaghetti code. One way this ends up happening without us knowing it, is by taking a 3rd party dependency (or dependencies) and integrating them into your project in an uncontrolled manner. Dependencies have a way of smearing themselves across a code base. You start by adding a dependency to one particular part of the code base, but then its types, function calls, and architecture slowly creep all over. Before you know it, you are dependent on that code 😅 and have a pile of spaghetti on your hands. A my work, we’ve had a hell of a time removing Firebase because it was everywhere. With nearly a year of chipping away at it, we are almost done.

If you are going to integrate a dependency, do yourself a favor and cage that animal — I like to think about it as designing code for deletion or replacement. By properly isolating a dependency when you bring it in, you set yourself up to more easily remove or replace it in the future and get the side benefit of more easily testing with that dependency (we even control calls to 1st party libraries to help with testing and mocking). There are many ways to accomplish this. This isn’t a post on how to do that, but to name a few.

There are other ways, but you get the idea.


Summary

There is a time to bring in a dependency, but blindly doing so can have a large impact down the line. For example, Firebase helped Driver in the early days, but we have spent nearly a year working to get rid of it 😅. Integrating a dependency is so easy. Sometimes just a couple of lines of code and you are good to go. However, like most things, there are tradeoffs. Because of this, we should be thoughtful about how we bring in our dependencies and take steps to protect ourselves from them.

Also, huge shoutout to R0ml. I have learned a lot of these ideas from him.


Thanks for reading. Feel free to reach out on Twitter — cheers!

rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora