Vendoring
# Vendoring
"Vendoring" most commonly refers to **copying third-party dependencies directly into your own source tree (or artifact store) instead of fetching them at build time**. If you meant something else — vendor management/procurement, or the vending-machine business — say so and I'll pivot. Below is the software-engineering sense.
## The core idea
Normally a build pulls dependencies from an external source (a package registry, a Git URL, a CDN). With vendoring, those exact sources are copied into a directory like `vendor/`, `third_party/`, or `deps/`, committed to your repo, and the build reads them from there. The build no longer needs the network or the upstream project to be alive.
Related but distinct concepts:
- **Lockfile** (`package-lock.json`, `Cargo.lock`, `go.sum`): pins *versions and hashes* but still downloads from registries.
- **Vendoring**: actually *stores the code*. A lockfile is a promise; vendoring is possession.
- **Mirroring / private registry**: you host a copy centrally instead of per-repo.
## Why people do it
- **Reproducibility** — builds work offline and years later, even if a package is deleted, retagged, or the registry goes down (the classic "left-pad" / deleted-npm-package problem).
- **Supply-chain integrity** — the exact bytes are reviewed and committed. Combined with a hash manifest, this defends against upstream tampering and gives you a place to audit or patch code.
- **Patch control** — enterprises frequently need to hotfix a dependency or apply a security patch before upstream ships one.
- **Build speed and hermeticity** — no network calls in CI, no flakiness from registry outages or rate limits; plays well with sandboxed/air-gapped builds.
- **Compliance** — easier to produce a software bill of materials and to satisfy license/audit requirements when all third-party code is in one place.
## Why people avoid it
- **Repo bloat** — vendored trees get huge, and code review noise drowns out real changes.
- **Upgrade friction** — updating means re-vendoring, and reviewers can't easily see what changed upstream.
- **Staleness and silent forks** — vendored code drifts; a local fix becomes a permanent unofficial fork nobody remembers.
- **Merge conflicts** — generated vendor diffs conflict constantly across branches.
- **Licensing hygiene** — you must keep license files and notices; it's easy to strip them accidentally.
- **False sense of security** — vendoring doesn't mean the code is *safe* or *current*; it means it's *frozen*. You still need vulnerability scanning.
## How it looks across ecosystems
| Ecosystem | Mechanism |
|---|---|
| **Go** | `go mod vendor` populates `vendor/`; builds use it automatically when present (`-mod=vendor`) |
| **Rust** | `cargo vendor` writes a directory + `.cargo/config.toml` source replacement |
| **PHP** | Composer historically installed into `vendor/` (usually gitignored); `composer install` from a lockfile is the modern norm |
| **Ruby** | `bundle package` / `bundle install --path vendor/bundle` |
| **Node.js** | No first-class vendoring; `npm pack` + local tarballs, `pnpm`'s content-addressable store, or committing `node_modules` (rare) |
| **Python** | `pip download`/`--no-index` wheels, `pip-tools` hashes, or a local wheelhouse |
| **C/C++** | Long tradition: `third_party/`, `extern/`, Git submodules, or tools like `vcpkg`/`Conan` |
Note the trend: many ecosystems *used to* vendor by default (Composer, Bundler) and moved to lockfiles + caches, while Go and Rust added explicit vendoring commands for people who need it.
## Best practices
1. **Treat the vendor dir as generated** — one command regenerates it; never hand-edit except as a clearly marked, tracked patch.
2. **Pin exact versions and include a hash/checksum manifest** (`vendor/modules.txt`, `cargo vendor` config, `vendor-manifest.json`).
3. **Preserve licenses and notices**; automate an SBOM (SPDX/CycloneDX) from the vendor tree.
4. **Keep a patch log** for local modifications, with the upstream issue/PR linked, so they can be dropped later.
5. **Scan vendored code** for vulnerabilities on a schedule — frozen ≠ safe.
6. **Exclude vendor dirs from linting, formatting, and coverage** so tooling and review stay focused.
7. **Vendor only what you actually ship**, and prefer vendoring + lockfile together rather than either alone.
## When it's the right call
Vendoring earns its cost when **reproducibility or control matter more than upgrade convenience**: long-lived/regulated products, air-gapped or hardened build environments, embedded and firmware work, anything with a decades-long support horizon, or organizations that must patch dependencies fast. For fast-moving application code with a healthy private registry and good CI caching, a lockfile plus a mirror usually gets you most of the benefit at a fraction of the maintenance burden.
---
If you had a specific ecosystem (Go, Rust, an air-gapped CI pipeline) or a specific angle (security, licensing, procurement) in mind, tell me and I'll go deeper on that.
Comments
Post a Comment
Thanks for you comment/s