Implicit Dependencies Are also Dependencies
A fairy tale.
'The not-so-fictituous project was developed in two countries. It was a large project, its functionality sufficiently complex to justify multiple processors / computers / servers for different portions of the job. For reasons of convenience, the project was split along the computer boundaries. Each development site became responsible for the software running on one computer. It had to fulfill their share of requirements, and to do their share of testing. As an interface between the computers, the communication protocol was specified according to the desired functionality.
'A few months later, each site declared victory: the software was finished. The integration team took over and plugged the components, errrh, computers together. They started working. A bit. Not much though: as soon as the most common scenarios were covered and the more interesting scenarios were tested, the interaction between the computers became unreliable.
'Confronted with this finding, both teams held up the interface specification and claimed their software would conform to it. Actually, this was found to be true. Both sides declared victory, again. No code was changed, and they developed happily ever after.
Who cares for dependency management?
Ten years ago, few people in industry projects did. Since then it became a commodity, a commonplace that is familiar even to product owners. Yes, software components have dependencies, particularly in large projects, particularly when you strive to increase your code reuse.
So now you have your dependencies: but you do not like them.
Dependencies make it hard to change code. Whenever you would like to change some code that others depend upon, you will experience lots of talk and work, and possibly strong forces of inertia when other developers would have to invest their time. This force grows very strong when you happen to dwell in environments with a lengthy development micro cycle, like in C++ projects or in embedded systems.
Many technical constructs have been invented to avoid dependencies. Parameters are passed in a string format, keeping the interface technically unchanged even when the string contents' interpretation changes. Component communication has replaced direct interface calls by a more anonymous bus where you do not need to contact your service yourself. It just needs to be out there somehow.
Here is the first warning: Obfuscated dependencies are also dependencies.
All technical independence does not relieve you (your project team) from taking care of it all. Changing an interface parameter's meaning is the same as changing the interface. You might have removed the compiler step, but you have not removed the need for deployment. Plus, you've added plenty opportunity for confusion that will haunt your during development, test, integration, and in the field — when you least expect it.
Looking at sound advice from software experts, you hear Fred Brooks talking you into conceptual integrity and Kent Beck advising to DRY (don't repeat yourself). While these concepts increase the clarity of your code and work against obfuscation, they also increase your dependencies. Now you have to refer to that one integer place from all your code. Good development practices increases your dependencies.
Second warning: Application dependencies are also dependencies.
With increasingly powerful code generation machines, it is very tempting to place the application knowledge into some model, and then to generate code that is independent of other code portions. All code fragments will live in technical independence. However, their combined behaviour is only meaningful if all the right code versions fit together, stem from the same model. So technical dependencies will not show you the entire picture: the implicit (i.e.: not visible in code) application dependencies are also dependencies.
Third warning: Do not make your code depend on some file that is intended to change as the project progresses.
It is very convenient to have a global file that, for example, lists all error codes in the system. When starting the new component, you proudly add a handful: look, I am a part of this! Only to find your colleagues moan about the extra build time. How dare you change this file?
Couldn't you just do what everybody does: reuse some error codes that are already in there, or reuse some error codes despite their intended meaning, and document the shift in meaning somewhere in your component documentation.