Mind your dependencies
Over the years I have changed my mind about adding dependencies to software. Through a number of painful lessons, I went from being over-confident to being over-cautious.
By dependencies I mean libraries, frameworks or technologies you add to your product because they cover some need you have.
How deep is the dependency going to be? I can think of 3 levels:
Core libraries. Libraries that, while not foundational, are widely used in your app and cover horizontal needs. For example, fixtures-support for testing, JSON-serialization systems, libraries for soft deleting records, etc.
Utility libraries. These are libraries you use in some places for specific needs. For example, a library for generating PDFs, an API client for some system, etc.
I have experienced the consequences of bad choices in the three groups mentioned above. Wrong picks for utility libraries are pretty dismissible, but wrong picks in the other two can become a severe burden.
It is very difficult to predict the future, but there are some things you can do. I will cover some in other sections, but a safe bet is to minimize the number of dependencies you add.
Popularity and stability
You want to aim for maximum popularity and stability. And both are dimensions that are often opposed:
- Can it be considered stable? Many major libraries make substantial changes after the first release. Do you want to get caught in the middle of such change? For example, both Ember and Angular introduced drastic changes to their programming models in version 2.
- Is there a big company using it? Or, even better, is there a company driving its development? Companies have real needs and resources. Developers working in their spare time are a much riskier bet.
- How many stars does it have on Github? I think that a thousand is the minimum you should aim for, but add a zero to that to that if what you are considering is a foundational technology.
- Is it a hot new thing everyone is talking about? If so, be double-cautious. Nobody knows the tradeoffs yet and the noise will make them look dismissible. And there are always tradeoffs.
Is the technology a good match for your organization? As obvious as it sounds, I have ignored this question more than once in my career.
Let me clarify with an example I know well. Have you thought about the consequences of adding native apps to your web platform? And what about creating single-page apps (SPA)? The theory looks simple: add an API and build apps that consume it, offering a delightful UX. In practice, it will have deep implications for your organization.
If you are a traditional small web company, you will have to multiply your team by 3, in the case or native iOS and Android apps, or by 2 in the case of SPA. They are just different apps. Of course, you can choose not to hire anyone. In that case, you will have to divide your throughput.
And notice I consider those numbers optimistic since, in software, complexity doesn’t raise linearly when adding people to the mix.
Also, intended or not, you will be encouraging teams of specialists instead of generalists: native apps, SPA framework, backend, API… This doesn’t play well with being (and staying) small.
Understand the tradeoffs
Make sure you understand the tradeoffs of your picks. And, in the case of foundational and core dependencies, your understanding should be intimate.
Let me continue with the example of adding native and SPA apps to your web platform:
- How your team productivity will be affected by the new native or SPA approaches? In my experience, productivity, compared to traditional server-side development, is orders of magnitude smaller.
- Are you sure you are getting a better UX? In both the native or SPA worlds, you better know what you are doing, or the UX will be actually worse. Slow load times come to my mind first, but there are many other things to consider. Managing a lot of state in the client and making everything move fast is incredibly challenging.
- Are you sure that a traditional approach of server-side-generated HTML with JS on top of it is not enough for most of your needs? I recommend you to check a modern stack like Rails 5. After years of working on SPA, I was blown away by both the performance and productivity of such approach.
- Your tooling will get more complex. Essentially you are adding new toolchains to your development and deployment workflows and, as with any other piece of software, they will require tweaking and maintenance from now on.
Just because I have been bitten for not doing it in the past, today I am a fan of gaining experience before making any commitment. Anything that lets you see how the code looks in realistic scenarios will place you in a better decision-making place (e.g: prototypes, proofs of concepts, pet projects, etc).
The key here is realistic. Creating some example todo app might not represent the real scenarios you will have to face in your app.
In the last months I have experimented with two technologies in personal projects: vanilla Rails 5 with all its goodies (Turbolinks, SJR responses, StimulusJs) and Ansible. Both have rewired my mind in many ways.
Consider building it yourself
I don’t consider a general Do-It-Yourself approach realistic for everything. But I think the opposite extreme, I will reuse-it-all, can be equally harmful for less obvious reasons. Often, when a library covers your needs, it seems that using it is a no-brainer. Again, make sure you are not ignoring the tradeoffs. In many cases, you only need a fraction of what the library does, so you might prefer something simpler, leaner and better tailored to your needs.
Say you want to soft-delete records in Rails. You can add that library that will be deeply wired into whatever version of ActiveRecord you are using. But what will happen if a new major version of ActiveRecord comes out and that library becomes unmaintained? Have you thought about the cost of creating such functionality yourself?
I think the same reasoning should be applied to most of your picks. You won’t obviously come up with your own web or testing framework, but there are many other things I have changed my mind about in recent years. For example, CSS and the need for a framework for it.
Just keep in mind what I said above: keeping your dependencies to a minimum is a safe bet.
I hate spending time integrating little third-party pieces together. And even more than that, I hate having to maintain all that glue as time passes and things stop working together.
When thinking in adding a dependency, the less glue the better. Even when composed of multiple parts, I strongly prefer cohesive solutions that configure everything for you. For example: I like Rails. I dislike React’s fragmented ecosystem.
Most of my career I have worked in product companies with a long-term vision for their platforms. I think this is a key factor. Spending a few weeks working on some core system instead of adding a new dependency you are not sure about can be acceptable in this scenario.
In other cases, the opposite approach might be more suitable. Say you want to prepare a quick prototype to see how some feature feels, or a MVP to validate your business idea. Moving fast, reusing as much as you can, makes more sense there.
As shiny as they might look, third-party dependencies always present tradeoffs and, sometimes, the implications are huge. Just make sure you understand the cost before making any commitment.