Glossary
Code dependencies describe how services and modules rely on each other—managing dependency chains keeps systems flexible and changes safe.
Across three companies — Shiksha Infotech, UshaOm, and Salesken — I've seen the same engineering challenges repeat. The details change but the patterns don't.
By the Glue Team
Code dependencies are relationships where one piece of code relies on another. A function depends on a library. A service depends on a database. A module depends on another module. Understanding dependencies is crucial for understanding impact of changes, managing complexity, and architecting systems.
Code dependencies include:
Change Impact: Understanding dependencies shows what breaks if you change something. High-impact dependencies need careful changes.
Architecture Understanding: The dependency graph IS the architecture. Understanding dependencies reveals how systems interconnect.
Bottleneck Identification: If many things depend on one thing, that's a bottleneck. Changes require careful planning.
Risk Management: Tight coupling creates risk. Loose coupling reduces risk. Dependencies show coupling.
Scalability: Circular dependencies and tight coupling limit scalability.
"Dependencies are always bad." False. Some dependencies are necessary. Goal is to manage them, not eliminate them.
"Fewer dependencies is always better." False. Some dependencies make code simpler. Goal is appropriate coupling.
"You can't change code with many dependencies." False. Changes are possible but require care. Tests protect you.
Coupling: How tightly dependencies bind code.
Modularity: Good design minimizes inappropriate dependencies.
Architecture: Dependencies define architecture.
Direct dependencies are explicitly declared. Your code imports library X. That relationship is visible and intentional.
Transitive dependencies are indirect. Library X depends on library Y, which depends on library Z. Your code indirectly depends on Z even though you never imported it. In a typical Node.js project, for every direct dependency, there are 10-50 transitive dependencies. This is why node_modules is famously large.
Runtime dependencies are needed when the application runs. Missing them causes crashes.
Build-time dependencies (devDependencies) are only needed during development. Missing them prevents building but doesn't affect production.
Internal dependencies are between your own modules. You control both sides. Changes require coordination but not external approval.
External dependencies are on third-party libraries and services. You don't control them. Updates, deprecations, and security vulnerabilities are outside your control.
| Metric | What It Measures | Why It Matters |
|---|---|---|
| Fan-in | How many modules depend on this one | High fan-in = high impact if it breaks |
| Fan-out | How many modules this one depends on | High fan-out = high risk of breaking when others change |
| Coupling | Strength of the dependency relationship | Tight coupling = changes propagate |
| Stability | Ratio of incoming to total dependencies | Unstable modules should depend on stable ones |
| Circular dependencies | A depends on B depends on A | Always problematic — refactor immediately |
Your dependency graph IS your system architecture, whether you designed it that way or not. Understanding it reveals:
When multiple libraries require incompatible versions of the same transitive dependency. Common in Node.js, Python, and Java. Solutions: lock files, version pinning, dependency resolution strategies.
Module A imports Module B, which imports Module A. This creates tight coupling, makes testing difficult, and can cause runtime issues. In JavaScript, circular dependencies can lead to undefined imports. Always refactor to break the cycle.
Code that depends on global state, environment variables, or implicit configuration. These dependencies aren't visible in import statements but cause failures when the implicit dependency is missing. Solution: make all dependencies explicit through dependency injection.
Adding dependencies for trivial functionality (the "left-pad" problem). Each dependency adds:
Rule of thumb: if you can implement the functionality in under 50 lines, consider writing it yourself.
For product managers: Dependencies affect feature scope and timeline. A feature that touches modules with many dependencies will take longer because changes propagate. Ask: "What modules does this feature touch, and what depends on those modules?"
For engineering managers: Dependency mapping reveals architectural health. If your dependency graph is a tangled web, velocity will suffer. If it is clean and modular, teams can work independently.
For CTOs: The dependency structure determines how well your codebase scales with team size. Highly coupled codebases require more coordination as the team grows. Loosely coupled codebases allow teams to work in parallel.
Q: How do you manage dependencies? A: Version carefully. Minimize surface area. Use dependency injection. Make dependencies explicit.
Q: What are dangerous dependencies? A: Circular dependencies. Hidden dependencies. Tight coupling. Dependencies on unstable code.
Q: Should you always minimize dependencies? A: Minimize inappropriate dependencies. Some dependencies make code clearer and more reusable.
Keep reading
Related resources