This text continues my attempt to speak directly to system owners, rather than to my usual community of software developers. Earlier texts in this series (“Feeling trapped in a US cloud?”) can be found here:

Today I will try to answer a question from a friend. They said:

“I get that it’s good to stay flexible and not lock yourself in for life with a certain provider. But how do you actually do that?”

In this text, I’ll focus on how to handle dependencies, especially external dependencies – meaning something you’re dependent on that another team or organisation is responsible for. Typical examples include databases, frameworks, code libraries, APIs, applications, and runtime environments.

Below are three practical tips you can bring into discussions with your developers on how to avoid ending up in a blind alley when it comes to dependencies. Make sure they don’t dismiss questions as “stupid” or “impossible”, but help you evaluate both past and future decisions more consciously.

Tip 1 – Avoid obvious lock-ins

Even though tips 2 and 3 are about handling tricky dependencies, the first tip of them all is to avoid obviously tricky dependencies altogether.

The obvious candidates are things that are unique to a specific product or vendor, for example:

  • A stored-procedure language tied to a single database vendor.
  • An uber-quick way of doing something thanks to a cloud tool unique to a single vendor.

At first, these choices might look like a good trade-off between a certain quick result now vs a possible cost later. After all, certain is better than possible, and now is more important than later, right? And sometimes that trade-off makes sense. But sometimes it’s just wrong.

When is it wrong, then? Business priorities and ethics will help judge that. From a development perspective, I also look out for two technical warning signs:

  • Will this choice affect testability negatively?
  • Will this choice prevent local development without a network connection?

If the answer is yes, you should reconsider.

Here’s a common objection: “Swapping database products, that never happens!” Well, I disagree. I’ve experienced it several times in recent years. In one case, for a very small solution, cost was the driver. Interestingly, the discussion about whether to swap took longer than the actual work, because the system had implicitly been prepared for it by following tips 2 and 3.

Another common situation worth considering is when there is only one vendor to choose from and you’re not allowed to read the code. In those cases, if not before, looking for an open-source alternative to evaluate is typically a good idea.

That said, open source is not automatically safe forever. Projects can change direction, open source can become closed source, or licenses can become problematic in later versions. So, even with non-lock-in open-source dependencies, follow tips 2 and 3.

Tip 2 – Keep your dependencies under control

Even if you avoid the worst lock-ins, you can’t avoid dependencies altogether – and you shouldn’t. Dependencies often create value. The key is to deal with them wisely and responsibly.

If a dependency is used in many places, aim to centralise its use by calling a single place rather than spreading it out in the code. That way, if you need to swap it, there’s at least only one place you need to change. (Some dependencies – such as to your cloud provider – might be harder to centralise. We’ll get back to that.)

Test automation is definitely your best friend when it comes to keeping your dependencies in control and staying flexible. You need automated tests that are testing real dependencies to check their behaviour, not just mocks. This allows you to try an alternative implementation and validate it using the same test suite. If the tests are green, you’re ready for more full-scale testing.

Your build pipeline should help you stay in control. Beyond running tests, it should also analyse your external dependencies to reduce the risk of security issues. Common practices include:

  • Generating an SBOM
  • Scanning for vulnerabilities
  • Verifying package integrity
  • Enforcing strict dependency-management policies, such as blocking deployments with critical security issues or license violations.

Your external dependencies are a key attack vector – treat them accordingly.

A short reflection: I was once asked to help a customer choose a product of a certain type. I told them they could pick whichever option they preferred from a short list. They found that unhelpful, they wanted me to tell them which one. When I said it didn’t matter, they thought I was being sloppy.

In hindsight, I should have explained myself better. What I meant was that the choice could easily be swapped later if needed. And the only way to learn whether the first choice was wrong was to live with it for a while.

Keeping dependencies under control doesn’t stop at development, it’s even more crucial during production. For that, observability is your best friend. Is the dependency (and the whole application) actually behaving the way we expect? At the end of the day, what happens with the dependencies in production is the truth.

Note: Not all dependencies are alike. A dependency on something another team in your organisation owns and controls typically carries less risk than one that’s fully external.

Another way to keep your dependencies in control is to describe infrastructure dependencies in code, using Infrastructure as Code (IaC). That doesn’t mean the code can handle any infrastructure, but it ensures the documentation used for inspection reflects reality: what’s actually running, not something outdated or guessed. To change the infrastructure, you change the code.

Once we’ve come this far, a dependency swap is starting to feel less like the end of the world and more like “just work”. But tips 1 and 2 aren’t enough on their own, we also need tip 3.

Tip 3 – Don’t let your dependencies infect you

When you depend on another model, for example an API, you can choose how to treat it.

If you conform to the model and use it everywhere in your own code, the dependency “infects" you. That makes the coupling much tighter than it has to be, and swapping the dependency later becomes much harder.

The alternative is to map at the edge and keep your inner code independent. In Domain-Driven Design (DDD) terms, this is expressed as preferring the Anti-Corruption Layer pattern over the Conformist pattern.

Somewhat related to this is preferring “necessary-evil”-use over “every-little-obscure-feature”-use of, for example, a framework The latter is a trap which is easy to fall into (been there, done that). It often starts off as wanting to learn all the ins and outs of something, but ends with burned wings. So, even if a new tool looks impressive, keep it simple!

One effective way to reduce this risk is to focus on a rich domain model for your core business logic, rather than letting a tool handle some of it. That means describing your business logic in a layer which is only about this and nothing else – definitely not mixed and affected with infrastructure dependencies. Done well, it can become so clean and clear that non-programming domain experts will be able to follow the most interesting code in discussions with developers.

Another way of reducing the risk is to favour tools with single purposes over “swiss army knife” tools. This idea of a component to do one thing and do it well is called the Unix philosophy. Multi-purpose tools are tempting – once you depend on it for one thing, it’s easy to just keep adding use cases. After a while, replacing it becomes very hard.

Any good shortcuts?

At this point, you might wonder if there are any good shortcuts for deciding which external dependencies to take on. Let’s explore a few.

A common shortcut is to choose a vendor and follow their path. I strongly dislike that and consider it a reliable way to end up trapped. I recommend evaluating each dependency individually each time. I touched on this subject in this text about preferring a tech-agnostic approach.

What about always writing your own code instead of adding external dependencies? Well, both have their advantages. Your own code:

  • Easier to fix immediately
  • Does exactly what you need – nothing more
  • You need to be in control, so you might as well own it

External dependency:

  • Proven and widely used
  • Faster time to value
  • Not primarily your responsibility to maintain
  • Often stronger from a security-review perspective

Unfortunately, this isn’t a good shortcut either. There’s no universal rule. You need to decide what is right in each specific case.

Final comment

To summarise, make your decisions regarding dependencies with wide-open eyes.

Dear @Community, do you have any additional tips for system owners (non-developers)?

Special thanks to Daniel Melin, Katharina Damschen and Daniel Raniz Raneland for their help with this text. Any remaining mistakes are mine.

This article was originally published on LinkedIn. Join the discussion and read more here.