2025-12-07
managing dependencies in software development
structural realities that shape dependency strategy
relinking requirements
- c requires every library that uses another library to relink against all transitive dependencies.
- a library linked against one version of another library cannot be safely combined with a library linked against a different version of that dependency.
- these constraints generate linker hell: version divergence forces rebuilds even when the direct interface has not changed.
incompatible c library ecosystems
- for example, glibc and musl cannot depend on each other. binaries linked against one cannot be mixed with components linked against the other.
- composition across libc boundaries requires complete recompilation of the entire dependency tree.
- similar incompatibilities appear when low level runtimes provide mutually exclusive abis.
compositional failure modes
- deep dependency chains increase rebuild frequency when any upstream segment changes.
- copy importers attempting to inline external code may face cascading rewrite requirements if nested dependencies are not flattened.
- binary level composition is constrained by abi coupling; source level composition is constrained by symbol layout and internal reference structure.
strategies for managing dependencies
direct inclusion models
- many libraries included anywhere: simple but prone to conflict when intermediate libraries include one another.
- importing and localizing libraries: source is copied into the project namespace to avoid external versioning issues.
- incorporating code portions: create a thin helper library containing only the subset of another library actually required. the code duplication can increase maintenance burden or require manual copy of the code for bugfix updates
flattening dependencies
- eliminating internal depth simplifies copy import.
- function level flattening: extract required functions of another library into a helper artifact so multiple modules share a consistent minimal subset.
- file level flattening: include large files even when only one binding is needed; maintenance cost stays limited to the actually used interface.
automated import and rewrite
- scripts can import files and rewrite internal references to local namespace identifiers.
- this avoids manual maintenance while allowing version pinning, deterministic builds, and isolation from upstream structural changes.
version pinning via vcs modules
- git submodules: users must run update or sync operations; dependency on repository layout and continued repository existence.
- submodules do not ship code by default; distribution requires extra steps or vendor scripts.
- hybrid patterns exist: track a pinned commit via submodule, then copy or vendor the corresponding code into the main tree.
- manual copying remains a valid choice when maintainers prefer controlled compatibility selection over tracking upstream evolution automatically.
common issues
- dependencies that are difficult to acquire or build (for example when any part of the systems becomes incompatible over time). this may fully prevent the usage of a program.