Author: Brandon Pearman

The views expressed here are mine alone and do not reflect the view of my employer.

Dependencies increase complexity because it adds more to consider. If developers are unsure of what hidden dependency will break, they will have to dig into every line of code to make sure it is safe. The less a component is aware or interacts with others the better.

"Developers who are afraid to change code because they aren't sure what might be affected. Systems with many unnecessary dependencies are very hard (and expensive) to maintain, and tend to be highly unstable."

-Pragmatic Programmer by Dave Thomas Á Andrew Hunt

Let's look at a simple example to explain why dependencies add to complexity:

  1. Example of a simple app: one which is fully self contained, depends on nothing. eg a html based site with no serverside functionality.
  2. Example of a complex app: one which depends on 100 different packages which needs updates and has version conflicts, it has to reach 20 different apis, it consumes messages from many different queues, it reads and writes from a db, it logs to files, other applications are dependent on it's API and messages it produces, etc.

Efferent coupling Á afferent coupling

Efferent coupling - what this component depends on

Afferent coupling - Other components which depend on this component

To make efferent coupling easier to manage, all dependecies must be injected via a classes constructor. That way a class can only be instatiated if it has all its dependencies and you can easily see what the class depends on.

While efferent coupling can be easier to manage afferent coupling is much harder to see.

High cohesion and Low coupling

Modules should not be tightly coupled to one another, so that it is easy to swap and change modules without interfering with eachother. In order to incorporate high cohesion and low coupling, you have to have a good understanding of the SOLID principles, more specifically of Single Responsibility Principle and Dependency Inversion Principle

"Dependencies on other classes within the same module are less harmful than those outside. Likewise, when two objects are naturally tightly coupled, multiple operations involving the same pair can actually clarify the nature of the relationship. The goal is not to eliminate all dependencies, but to eliminate all nonessential ones."

-Domain Driven Design By Eric Evans

Cut out the dependency

If you can do without the dependency, scrap it. Often developers throw in every possible small tool they can find because it does something small or because the default is "not good enough". Adding an extra package, tool, dependency must be an absolute last resort, not the first thing you do.

Improve Visibility with explicit dependencies

"Even within a MODULE, the difficulty of interpreting a design increases wildly as dependencies are added. This adds to mental overload, limiting the design complexity a developer can handle. Implicit concepts contribute to this load even more than explicit references."

-Domain Driven Design By Eric Evans

Each module may or may not have dependencies on other modules or libraries. Make sure that the dependencies are well defined and explicitly mentioned so that it is very clear what is requiree to run a compotent/module/class.

Hidden dependencies can make any form of change very difficult. Do NOT hide dependencies by instantiating them in random places scattered throughout the code base. Rather keep the full list of dependencies in one place by instantiating them in one place or by injecting them via a DI container, so that the dependencies are clear. Dependency injection is one way to make dependencies more explicit, because you can't create and use the code without injecting its dependencies.

uni-directional flow of dependency

Make sure the flow of dependency is uni-directional, it should be easy to reason about the flow of dependencies without explaining the details of the code. When you open an application you have never seen before and it's structure represents the flow of dependencies, the overall picture of the project becomes far easier to understand. Compared to a structure where anything could be dependent on anything and the project structure does not represent a clear flow.

Dependencies should be encapsulated and have in Intenet Revealing Interface

David Lorge Parnas developed the concept of information hiding in modular programming. Each module should hide their inner workings and only expose what needs to be public in a fashion which makes it's use easy. Having a well thought out public interface will save you a lot of trouble. Encapsulation is one of the principles of Object-Oriented Programming which you should take a look at as a refresher.

To ensure that you don't break any clients of your interface, it is important to understand and apply Liskov Substitution Principle and Design By Contract

Check out these links for more info:

My design and architecture repo