Skip to main content
Test Double company logo
Services
Services Overview
Holistic software investment consulting
Software Delivery
Accelerate quality software development
Product Management
Launch modern product orgs
Legacy Modernization
Renovate legacy software systems
DevOps
Scale infrastructure smoothly
Upgrade Rails
Update Rails versions seamlessly
Technical Recruitment
Build tech & product teams
Technical Assessments
Uncover root causes & improvements
Case Studies
Solutions
Accelerate Quality Software
Software Delivery, DevOps, & Product Delivery
Maximize Software Investments
Product Performance, Product Scaling, & Technical Assessments
Future-Proof Innovative Software
Legacy Modernization, Product Transformation, Upgrade Rails, Technical Recruitment
About
About
What's a test double?
Approach
Meeting you where you are
Founder's Story
The origin of our mission
Culture
Culture & Careers
Double Agents decoded
Great Causes
Great code for great causes
EDI
Equity, diversity & inclusion
Insights
All Insights
Hot takes and tips for all things software
Leadership
Bold opinions and insights for tech leaders
Developer
Essential coding tutorials and tools
Product Manager
Practical advice for real-world challenges
Say Hello
Test Double logo
Menu
Services
BackGrid of dots icon
Services Overview
Holistic software investment consulting
Software Delivery
Accelerate quality software development
Product Management
Launch modern product orgs
Legacy Modernization
Renovate legacy software systems
Cycle icon
DevOps
Scale infrastructure smoothly
Upgrade Rails
Update Rails versions seamlessly
Technical Recruitment
Build tech & product teams
Technical Assessments
Uncover root causes & improvements
Case Studies
Solutions
Solutions
Accelerate Quality Software
Software Delivery, DevOps, & Product Delivery
Maximize Software Investments
Product Performance, Product Scaling, & Technical Assessments
Future-Proof Innovative Software
Legacy Modernization, Product Transformation, Upgrade Rails, Technical Recruitment
About
About
About
What's a test double?
Approach
Meeting you where you are
Founder's Story
The origin of our mission
Culture
Culture
Culture & Careers
Double Agents decoded
Great Causes
Great code for great causes
EDI
Equity, diversity & inclusion
Insights
Insights
All Insights
Hot takes and tips for all things software
Leadership
Bold opinions and insights for tech leaders
Developer
Essential coding tutorials and tools
Product Manager
Practical advice for real-world challenges
Say hello
Developers
Developers
Developers
Software tooling & tips

The dangers of abstracting away vendors in code

When integrating third-party vendors, abstraction is key—but too much can make your code unnecessarily complex and inefficient. Here's how to strike the right balance.
River Lynn Bailey
|
November 19, 2024
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

A coworker recently asked about writing a wrapper around a third-party vendor and their feature set. The concern was how much abstraction to add around the vendor API - wondering if they should make the wrapper a simple pass-through to the vendor, or if they should try and create a more complex abstraction that happened to use the vendor's API.

The ultimate goal with this conversation was to determine if the client should be worried about vendor lock-in. If they abstract the vendor-specific code into something more generalized, it should be more resilient to vendor changes, right? While there is some truth in this, it can be a dangerous path.

If we're building something with the intent of quickly and easily dropping one provider in favor of another, there's a very real danger of writing an abstraction that only does the following:

  • Creates an unfortunately complex abstraction
  • Makes our code very difficult to understand, and
  • Codes to the "lowest common denominator" between possible vendors

The first two are problems that we, as engineers, generally love to solve, and can often do very well — it's that last one where we find the real danger. 

Coding to the lowest common denominator of features that vendors provide is going to ensure you have a generic, difficult-to-use, and feature-missing (not feature-rich) API that doesn't provide the level of value that your chosen vendor is capable of.

Coding to the Lowest Common Denominator?

If there are multiple vendors that can provide this service, there is a strong likelihood that each vendor offers something unique. Otherwise, there probably wouldn’t be so many vendors, and their expertise would likely be a commodity, open-source, or even built into a programming language.

With that in mind, consider writing a generic abstraction around the feature you need and doing so in a way that makes it easy to drop one vendor for another. It's a tempting thought and is something I have tried to do many times in the past.

Let's say you have two vendors you're choosing from, though. There may be some similarities in the solutions, and there may be some very different solutions, different features, and specializations that make one vendor stand out above the other for some scenarios.

Vendor A:

  • OCR
  • Hide PII (personally identifiable information, like name and birthdate, phone, etc)
  • AI to hide irrelevant data

Vendor B:

  • OCR
  • Provide custom categorization and ML to learn about them
  • Correlate multiple digitizations across many users to better understand the images and categories using AI

If I try to write something so abstract that I can drop a single vendor in favor of another because I only coded to the lowest common denominator, what am I left with? Just basic OCR. 

In that situation, why am I bothering to use a vendor for that when OCR libraries are freely available? The value that both of these vendors provide isn't the commodified OCR capability. It's the additional feature set and expertise around the OCR.

Coding the vendor into your app, directly?

The other side of this coin is to code something so specific to a vendor that it ends up "infecting" the entire codebase with that vendor's terminology, toolset, libraries, etc. This can effectively make it impossible (or at least impractical) to change vendors.

If the vendor ends up not providing the services that are needed, if the vendor ends up shutting down, or for any number of other reasons, this is a dangerous path. Having code that is tied to a specific vendor, throughout the system, means that the vendor is locked in to the system. It can take months or even years to correct this.

What, then, is the right way to approach an abstraction or other use of a vendor?

Coding to your app's current needs

The trick, and where the real value lies in what that we provide as consultants, is to find the appropriate balance between vendor lock-in and the lowest common denominator. 

My goal as a developer is to write code that targets the required feature set and capabilities of my system, while pushing the vendor specifics into the implementation in a way that allows me to still take advantage of the vendor's unique features and capabilities.

Instead of throwing out all caution and running with vendor lock-in as the norm, though, it's worth taking time to classify the vendor specific features as features your system provides. Keep the vendor specifics consolidated in and hidden behind your API, but keep them close to the surface of the API, so you don't end up with a monstrosity of complex code.

When it comes time to consider changing providers or tools, I at least have an API level abstraction to compare against. There's a very strong chance that I'll end up losing some features and deleting that code — but I will likely gain some features and add new code, too.

I'm not trying to find the ultimate, perfect abstraction that makes it easy to drop a vendor. But I'm also not trying to make my code work with just any vendor. There will be specifics for a vendor that I want to code against. At the same time, there should be some level of an API layer between the vendor and the rest of the system.

Fellow Test Doubler, Tom Nightingale, said it this way in that same conversation:

I try to think about the interface that our application requires to use the behavior provided by the 3rd-party.
If the interface is built in a behavior-centric way, it can be adapted to a new vendor in the future and will remain in the codebase as long as the feature using it exists.
This approach can also help avoid completely encapsulating the 3rd-party library and causing the "lowest common denominator" issue. If your application needs another feature offered by the 3rd-party then you're free to use that independently of what you have built to-date.

Good, Not 'Best,' Practices

There is no perfect solution here, because there are no truly "best" practices for all situations. And there's no "right" or "wrong" way to find the needed balance when bringing a new vendor or tool into your code, without understanding all of the details and complexities in an application, what it needs to do, and why.

But if I look at vendor-based solutions from this perspective, the conversation shifts from "what abstraction can I write to make this easier for switching?" to "what features does this system need from this provider, and potentially other providers being considered?" 

In the end, it becomes a product conversation before it's a technical one. It also means the ability to change vendors becomes something to prioritize and migrate toward, not something to "plug-n-pray" about.

Want more tips, tricks, and insights to level up your software game? Sign up for the Test Double Dispatch and get the latest resources delivered right to your inbox.‍

Software tips and tooling, straight to you

Subscribe to the Test Double Dispatch for real talk, coding tips—delivered to your inbox every month.

Sign up now

Related Insights

🔗
14 tools every Ruby developer will love for performance and debugging
🔗
Best command-line tools, according to our software consultants
🔗
Best books and resources for software developers

Explore our insights

See all insights
Leadership
Leadership
Leadership
The business of AI: Solve real problems for real people

After participating in the Perplexity AI Business Fellowship, one thing became clear: the AI hype cycle is missing the business fundamentals. Here are 3 evidence-based insights from practitioners actually building or investing in AI solutions that solve real problems.

by
Cathy Colliver
Leadership
Leadership
Leadership
Pragmatic approaches to agentic coding for engineering leaders

Discover essential practices for AI agentic coding to enhance your team’s AI development learning and adoption, while avoiding common pitfalls of vibe coding.

by
A.J. Hekman
by
Aaron Gough
by
Alex Martin
by
Dave Mosher
by
David Lewis
Developers
Developers
Developers
16 things software developers believe, per a Justin Searls survey

Ruby on Rails developer Justin Searls made a personality quiz, and more than 7,000 software developers filled it out. Here's what it revealed.

by
Justin Searls
Letter art spelling out NEAT

Join the conversation

Technology is a means to an end: answers to very human questions. That’s why we created a community for developers and product managers.

Explore the community
Test Double Executive Leadership Team

Learn about our team

Like what we have to say about building great software and great teams?

Get to know us
Test Double company logo
Improving the way the world builds software.
What we do
Services OverviewSoftware DeliveryProduct ManagementLegacy ModernizationDevOpsUpgrade RailsTechnical RecruitmentTechnical Assessments
Who WE ARE
About UsCulture & CareersGreat CausesEDIOur TeamContact UsNews & AwardsN.E.A.T.
Resources
Case StudiesAll InsightsLeadership InsightsDeveloper InsightsProduct InsightsPairing & Office Hours
NEWSLETTER
Sign up hear about our latest innovations.
Your email has been added!
Oops! Something went wrong while submitting the form.
Standard Ruby badge
614.349.4279hello@testdouble.com
Privacy Policy
© 2020 Test Double. All Rights Reserved.

Software tips and tooling, straight to you

Subscribe to the Test Double Dispatch for real talk, coding tips—delivered to your inbox every month.

Sign up now