I think the answer is both yes - and no.
…but what you really should be keeping an eye out for are interdependencies in your software (not necessarily talking about package dependencies here). If you become diligent about reducing interdependencies, I believe the question about package dependencies will simply boil down to “is this package of good quality - or should I build my own”.
Why do we want dependencies?
In my experience, code becomes much cleaner/easier to maintain with a careful construction of the software layers.
When you start writing code, it is really easy to build a solution where multiple components are interdependent. This situation causes alot of headaches when the complexity of a program increases. Developers have trouble deriving a good mental model of the solution - and then the implementation starts to degrades.
Layering is a good way to mitigate this problem. If you need a particular feature - like NaN math - then you should push it to its own “subsystem”. Ideally, that subsystem should only depend on what it absolutely needs in order to work. If you do this well, I believe you naturally start developing packages similar to those of Tim Holy, that “Do One Thing” (as @Tamas_Papp stated).
→ Whether or not you use a 3rd party package, build your own, or integrate that “subsystem” directly in the software you are deploying, the important part is to layer things properly. By “properly”, I guess I mean that the solution just feels natural and appropriate (not forced) given the problem at hand.
This is not as trivial as you might think. Most people will probably have to put alot of thought into their software in order to reduce this interdependency. It is actually quite annoying, because the solution just seems to make alot of sense when you build it well… but I can honestly say that my own solutions are far from harmonious when I start writing them.
Why do we want to avoid dependencies?
In order to remove interdependencies, we must try to eliminate cyclical dependencies.
A good example of this resides in most modern GUI toolkits I have used myself. Modern toolkits use some sort of callback mechanism that show up with different names:
- Callback functions
- Signal/slots
- Event handlers (MS products)
These “event handlers” (I sort of like this name) are a great way to decouple widget code from that of the user application. A widget does not have to know anything about the target application in order to be useful.
How to reduce interdependencies in Plots.jl?
Well, one example that comes to mind is to build a clean interface to plots that does not depend on any of the backends directly. Let’s call this module PlotsBase
.
The next layer up would simply be the implementation of the PlotsBase
interface. Moreover, to avoid being stuck with a master module that knows about all plotting backends, one would have to create seperate modules for the different implementations:
(PlotsBase, GR) <- GR_Plots
(PlotsBase, PyPlot) <- PyPlot_Plots
(PlotsBase, InspectDR) <- InspectDR_Plots
...
But what about the users that what a plug-and-play experience - and don’t want to pick their plotting backend with a using
statement, but would rather call backend(:gr)
?
→ They can instead call the higher-level wrapper module called Plots
:
(PlotsBase, GR_Plots, PyPlot_Plots, InspectDR_Plots, ...) <- Plots
What’s so good about this new Plots
hierarchy?
If a user/company now wants to build their own private plotting backend compatible with the PlotsBase
module, they can do so without having to maintain a modified version of the Plots.jl
package. They can just use PlotsBase
as-is, because PlotsBase
does not require implementation details of the plotting backends.