Michael Eriksson
A Swede in Germany
Home » Software development | About me Impressum Contact Sitemap

Dependencies

Disclaimers for the passage of time

  1. At repeated points below, statements are made about specific technologies, notably KDE. These held true at the time of original writing, usually somewhere in the range 2009–2012; however, I make no guarantees for the time of reading. (Ditto, to some degree, my personal situation. Note e.g. the Opera/Firefox discussion below.) While I have not looked into what changes might apply, I do note a tendency for software to grow worse in various regards, notably undue bloat, over time.

    As the big-picture principles are what matter, this is of secondary importance.

  2. At the time of original writing of parts of the below, I was not yet aware of how much some Linux distributions, notably Debian, mess up, implicitly remove build options present “upstream”, etc. (A switch to Gentoo has been very enlightening in this regard.)

    Also, but more a matter of sloppy writing, some statements about installations and deinstallations might reflect the behavior of apt-get/Debian to an undue degree.

    I will try to expand on this at some future time. For now, just note that it is often the case that a Linux application does have the ability to rid itself of some dubious dependencies/features, but that some distributions, notably Debian, only provide “almost everything included” builds over the official repositories. Of course, when libraries are built to require every conceivable dependency on their own, a chain reaction of installs can follow.

  3. I made considerable-but-incomplete additions to this page during a lengthy absence from the Internet. These went unpublished until 2023 and a website overhaul; however, remain incomplete even after publication, as I no longer remember all that I had intended to write and do not have the time to write it anyway. (One point, however, is to keep dependencies to the minimum number of stages. For instance, a runtime dependency that is not needed at build time should not lead to a build failure.)

    Another side-effect of these incomplete changes is an odd “chapter structure”, the correction of which will have to wait.

Keep dependencies to a minimum

Dependencies should be kept to a minimum, be properly abstracted, and only prevent build (installation, running, whatnot) when they are actually vital to the build (etc.)—not a secondary nice-to-have. This is not limited to third-party software, but includes inter-module and inter-library dependencies within a single organisation or project.


Side-note:

This discussion may seem odd to a Windows user, who is used to get all applications in one giant, bloated installation. The rest of the world has much more efficient install-on-demand solutions with automatic dependency management available. Consider apt-get, Maven, CPAN, ...


Examples

Gratuitous but bloating feature

Example

A developer has written a small CLI tool to make rudimentary sorts. As an afterthought, he has the idea that it would be neat with a small GUI, where the CLI-fearing could copy-and-paste their data and click on a sort button. He does this using a third-party GUI library, which is several orders greater in size than the tool, and which in turn has other dependencies. Another developer tries to install this tool, intending just to use it on the command line—only to find that he can only do this if he also installs the GUI library, the libraries it depends on, and possibly further recursive dependencies. In the end, he has spent half-an-hour down-loading and installing several hundred MB. This to run a program of just a few dozen lines of code—and which he could have written himself from scratch in the same time it took to do the downloads.

What to do instead

How should the original developer have proceeded? Firstly, he should have strongly considered whether the GUI was a good idea at all. Gratuitous GUIs bring little benefit, and GUIs should only be added when there is a serious chance that they will be needed. Secondly, the inclusion of the GUI should have been made conditional, e.g. by dividing the package/module in two parts, one with the core functionality and one (optional) with the GUI. Notably, this has the added benefit of making switches to other GUI-libraries easier, as well as simplifying ports to other platforms.

Note also the concept of indirection: Having a dependency on an abstract GUI interface (which would hide the actual implementation) would reduce the problems noticeably. For one thing, a dummy implementation can be provided for those who do not need to actually use the GUI; for another, switching implementation is easier, as above.


Side-note:

The use of GUI as example is unimportant (but a common case in real-life): Corresponding problems exist with very many “nice-to-haves” and unimportant add-ons that most users will be happy to do without.

(However, the option of using two parts might be far more relevant for GUIs, especially when multiple GUI frameworks are to be supported and/or separate applications can be made. For many other cases, another approach might be better.)

The same principles apply here: Consider whether the feature is actually needed. If not, it is likely best not to include it in the first place. If it is included, but not central, use a solution with an optional add-on, a plug-in, or similar.


Dependency on KDE

Problem

Under Linux it is regrettably common that someone writes a potentially interesting application—and then arbitrarily ties it to the KDE desktop environment. (Similar claims apply elsewhere, but KDE is particularly problematic. Note the difference to the above discussion: there, it was a matter of GUI or no GUI; here, it is a matter of GUI with KDE or GUI without KDE.)

Now, such an application can be used without actively using KDE (a user-hostile and limiting Windows-wannabe); however, starting any for-KDE application automatically starts large parts of the KDE infrastructure—most of which brings no benefit whatsoever to the users of the application, but which wastes resources and introduces the possibility of mischief going on in the background. Indeed, I tend to keep my computers clinically free of anything belonging to KDE for these very reasons—with the side-effect that individual applications with such an arbitrary KDE dependency will no longer run.

What to do instead

First, write the application using a generic (possibly own) adapter interface to a low-weight and unintrusive GUI library with good availability (e.g. GTK under Linux). At this time, the focus is on making the application actually do what it should and to do it well.


Side-note:

While this is mostly a question of priorities (do the important things first), it can also help with keeping simplicity where simplicity is beneficial, e.g. through reducing the temptation to add one-thousand-and-one graphical bells-and-whistles, use various unrelated (non-graphical) features of e.g. KDE, etc. The application will be better for it.


In a second step, get to work on polishing the details of the GUI, still with the low-weight library, still with the adapter.

In a third step, if time allows and the benefit is suspected to be non-trivial, write an additional adapter for KDE that the user can chose to activate instead of the original adapter. (Note that the application itself is not changed, relying only on the identical public interface of the adapters.) The application is now independent of the GUI libraries, while the overall dependencies follow from what adapter the user chooses.

Notably, the use of KDE-libraries bring no benefit when the user is not actively using the entire KDE as a desktop environment. Correspondingly, an open-source application would do well in taking it even further: If some users want the third step, they should take it themselves, leaving the main developers to do more worthwhile things.


Side-note:

Note that GTK applications run with no problems under KDE. They might look a little differently, might not integrate into this-or-that KDE-bar, whatnot; however, these are small limitations—and, frankly, more a sign of how conceptually flawed and anti-integration KDE is...

KDE (and to some extent Gnome) is about building a system-within-a-system and raising barriers contrary to the philosophy of Unix. Cf. my discussion of IDEs for a similar problem—but note that KDE has far fewer benefits to offer than the typical IDE.


Dependency on a sound system

Problem

Many applications that have nothing whatsoever to do with sound insist on having a dependency on a sound system to do something entirely trivial, of little or no value to the user, and/or something that the user often sees as negative and prefers the application not to do in the first place. A typical example is making an annoying and gratuitous beep when the user commits what the application considers to be an error.

Even when sound settings are explicitly disabled, these applications typically waste time probing for a sound system, often give warnings messages to the effect that no sound system could be found, and in rare cases even refuse to run.

Worse: Sometimes (as with KDE above) there is a dependency on a specific sound system, e.g. Arts, and when the user tries to deinstall that sound system, already having his sound needs met, he finds that this is impossible without also deinstalling the application...


Addendum:

As of 2023, the use of Pulse is likely the main culprit. TODO integrate and expand on old Wordpress writings on Pulse vs. Alsa.


What to do instead

Firstly, consider whether it is a good idea to use sounds at all: My personal preference is that an application should never make any sound of any kind whatsoever (unless sound is a part of its actual core tasks, as with e.g. a media player), and this preference is shared by very many users. Of the remainder, I suspect that the clear majority are neutral and could easily do without sound, leaving only a small minority who positively want or need the sounds.


Side-note:

I recall opening up a handheld Nintendo game in the mid-1980s in order to disable that annoying and incessant beeping. I found a (in my then perception) weird metal disc attached to two cables, hesitated, uncertain both whether this disc was truly the source of the sound and whether cutting the cables could have negative side-effects, and then proceeded. Game play was suddenly much more pleasant.


Secondly, if sound is still wanted, make sure that there is a “deactivate sound” setting. If activated, the application should not even try to do anything sound related—not even probing for the existence of a sound system.

Thirdly, when sound is activated, use some form of adapter interface (as above) to be able to use different sound systems and to make sure that the application still runs if none is available. (E.g through using a dummy adapter which does nothing when called, except returning a “successful”.) This also decouples the application sufficiently that present sound systems can be deinstalled without problems.

Libraries are not magical

Generally, one should always bear in mind that libraries are not magical: If a dozen lines of own code calling a library replaces several thousand lines of entirely original code, then those several thousand lines are still present in the library—typically with thousands of other lines that are not used.

Do use libraries

The above does not mean that libraries are to be avoided: On the contrary, libraries are a wonderful thing—and re-inventing the wheel to avoid using an existing library is rarely justifiable.

The above centers on misuse, gratuitous use, and lack of insight into consequences: Use a library because it extends the application in a truly worthwhile manner, or to avoid a redundant implementation of existing functionality; not because it is there, would make it easy to add a feature of disputable value, or similar.

Try to use strict hierarchies

In particular in own development, it pays to use strict hierarchies of who can call whom, and to enforce these hierarchies with build tools and other checkers. Consider the following simplified example:

A company has a standard platform (sp) which is used as the basis for several projects. One project is pr. The standard platform provides the packages
sp.util,
sp.user,
sp.useradmin;

and the project

pr.util,
pr.user,
pr.useradmin.

One reasonable hierarchy would be to take the packages in that order: pr.useradmin may call e.g. sp.useradmin, sp.util, and pr.user. However sp.useradmin may only call sp.util and sp.user; pr.user may not call pr.useradmin, but e.g. pr.util; etc.


Side-note:

Obviously, in particular for complex projects, tree-like hierarchies will be the rule, e.g. where packages A and B are both allowed to depend on package C, but neither is allowed to depend on the other (nor C on either of them). That the above is a linear hierarchy is just a side-effect of the simple example.

Also note that the risk for an inadvertent cycle is greater in a tree, making the use of automatic dependency checkers particularly valuable.


This makes it easier to avoid conflicts, keeps modularization higher, makes it easier to replace or remove components, etc. In particular, it removes the risk that one project becomes indirectly dependent on a second project—a dangerous sloppiness that regularly occurs in inexperienced organisations.

Excursion on module-based designs

An increasingly common design is to use a smaller, sometimes minimal, core with any non-core functionality handled by optional modules, plug-ins, or add-ons. This is normally a good idea; however, it is often handled poorly from a user’s POV. Typical errors include:

  1. Failing to include central functionality in the released basic versions. A good example is Firefox, which lacks e.g. the ability to mask the referrer or to selectively block content using using URL patterns—both things that any modern browser should provide out of the box.

    That these functionalities are delegated to add-ons from an architecture POV could, taken on its own, be correct; however, that the user has to download them himself after installation is not. To make matters worse, it is my impression that the functionality is only available through third-party add-ons. (I may be wrong, however, being mainly an Opera user.)


    Side-note:

    Firefox does have a built-in image blocker. This, however, is a very ungeneric and unsatisfactory substitute.



    Addendum:

    This was true long ago. Since then, the deterioration of Opera made me switch to first Firefox and then the Firefox-derivate Tor Browser. In the mean time, Firefox, too, deteriorated ever further, including many instances of functionality disappearing entirely, being moved from the main configuration to about:config, or being forced into new plugins—and, at some point, the old plugin framework was axed, killing most plugins that dealt with configuration enhancements. In 2023, Firefox is a significantly worse and more user-hostile browser than, say, ten years earlier.


  2. Including too many nice-to-have modules, making the application unnecessarily bloated and hard-to-overview—and removing many of the advantages of a modular approach. Far too many companies try to force-feed the user every last feature they can think of—whether he will benefit from and want them or not.


    Side-note:

    Is this not a paradoxical claim in light of the previous item?

    No, not really: Firstly, the features mentioned for Firefox are “due diligence” issues. I cannot recall the last time I used the “spray water” function on my iron—but I would still consider any iron not having one to be defect. A built-in mp3 player, OTOH, that is truly just a nice-to-have. Secondly, these features are logically far closer to the core than most other add-ons. A control for the amount of steam that an iron gives off is close to the core functionality of an iron, both conceptually and implementation-wise. If it is available, it should reasonably be available with the out-of-the-box product—not over an add-on to buy separately. (And, no, I cannot recall when I last changed the steam setting either.)


    Addendum:

    As of 2023, I cannot recall the last time that I used an iron—period. Does that mean that all features of the iron should be scrapped? No!



  3. Not providing proper controls against unwanted dependencies (for third-party developers) and automatic installation of dependencies (for users). A third-party developer should be kept in sufficiently strict reins that there is no risk for circular dependencies, small add-ons that drag a long tail of other add-ons with it, and so on—at least for add-ons that are distributed through official and semi-official channels. A user (working with an official repository) should never have to manually install dependencies; this should happen automatically (after his confirmation that he still wants to install the add-on, even with the need for further items).