Operator! Get me the president of the world!
Dependencies are great. They allow developers to reuse code, a pillar of software engineering. This can be code developed by another company, like the omnipresent Google Guava library. Or it can be an internal library that developers publish for other teams.
Dependencies are scary. Dependencies tend to have dependencies. Managing this graph of dependencies is impossible.
An example is the "Diamond Dependency Problem". Library A depends on libraries B and C. B and C both depend on library D, but different versions of D. What version of library D should library A use?
Programmers have implemented different strategies to deal with the "Diamond Dependency" and none have solved it. Build tools like Maven are able to recognize the problem. But after recognizing it, Maven picks a version (by default the newest) and tosses it on the classpath. No guarantee that version will work and failures often occur at runtime.
NPM takes a different approach. Each dependency has a copy of all its dependencies and these copies are not shared. Library B has its own library D and library C has its own version of D. This is kicking the can down the road. Now developers discover breaking changes at runtime in horrible fashion. For example, library A asks for a model object from library B and passes it to library C. What if library D defines this model? There are now two versions of library D's model floating around which can lead to awful serialization problems.