Vendoring—copying third-party code into your repository—destroys version control benefits, creates divergence, and makes updates impossible. Git submodules and dependency management tools offer better alternatives.
The practice of "vendoring" has quietly become one of software development's most insidious anti-patterns, undermining decades of progress in version control and dependency management. At its core, vendoring involves copying third-party code from external repositories into your own source control, creating a tangled web of duplicated code that's difficult to maintain and nearly impossible to update properly.
The Seductive Appeal of Vendoring
The concept begins innocently enough. Developers want to ensure their applications work consistently across environments, so they copy dependencies into a /vendor directory. This guarantees that the exact versions of libraries, frameworks, and plugins used during development are available in production. The problem isn't the deployment strategy itself, but what happens next: developers commit these copied files to their own repositories.
This practice is more destructive than simply duplicating functions instead of using proper inheritance and abstraction. When you vendor code, you're not just copying a few lines—you're duplicating entire codebases, complete with their own histories, bugs, and security vulnerabilities. You're creating a fork without meaning to, and without any of the tools that make forks manageable.
The Hidden Costs of Code Duplication
Once you've vendored dependencies, you've lost the fundamental benefits that version control systems provide. All history, branches, and tags from the original repository vanish. You can't easily pull updates when security patches are released. You've created an environment where divergence becomes inevitable—your vendored code will slowly drift away from the canonical source as you (or others) make modifications to fix bugs or add features.
Space efficiency becomes a casualty as well. Instead of storing a single copy of a dependency that multiple projects can reference, you're storing complete duplicates in every repository that uses them. This might seem trivial until you're dealing with large frameworks or multiple dependencies across dozens of projects.
The most insidious aspect is the difficulty of tracking provenance. Months or years later, when you need to know which version of a library you originally copied, or where that code came from, you're left with a detective story. Unless someone meticulously documented the source and version at the time of vendoring—which almost never happens—you're flying blind.
The Better Way: Git Submodules and Modern Dependency Management
Git submodules provide exactly what vendoring attempts to achieve, but without the destructive side effects. A submodule stores a URL and commit hash in your repository, creating a reference to external code that remains under version control. You get the benefits of pinned dependencies without losing the ability to track updates, merge changes, or understand the relationship between your code and its dependencies.
For teams not using Git or needing more sophisticated dependency management, modern package managers have evolved to handle these scenarios elegantly. Tools like npm, Cargo, Maven, and others allow you to specify exact versions of dependencies, create lock files that ensure reproducible builds, and manage transitive dependencies automatically. These systems were designed specifically to solve the problems that vendoring attempts to address through brute force.
When Vendoring Seems Necessary
There are edge cases where vendoring might seem like the only option. Perhaps you're working with a dependency that doesn't have a proper package repository, or you need to modify third-party code in ways that upstream maintainers won't accept. In these situations, the solution isn't to abandon version control best practices entirely, but to be more deliberate about your approach.
If you must vendor code, at minimum: document the original source repository and exact commit hash, isolate vendored code in clearly marked directories, and establish a process for periodically checking for upstream updates. Better yet, consider whether you can contribute your changes upstream, fork the repository properly, or find alternative dependencies that better fit your needs.
The Path Forward
The software development community has spent decades building tools and practices to manage dependencies effectively. Vendoring represents a step backward, trading long-term maintainability for short-term convenience. Every time a developer commits vendored code to a repository, they're not just creating technical debt for their current project—they're contributing to a culture that accepts poor practices and makes software maintenance harder for everyone.
The next time you're tempted to vendor a dependency, ask yourself: am I solving a real problem, or am I creating ten more? The answer, more often than not, is that you're creating problems that will haunt your project for years to come. Choose the tools designed for dependency management. Your future self—and every developer who works on your code after you—will thank you.

Featured image: The tangled web of vendored dependencies, where code duplication creates maintenance nightmares

Comments
Please log in or register to join the discussion