I hear the terms "Dynamically linked" and "Statically linked" tossed around in regards to packaging, and I have never been clear on what these terms actually mean.
Are Snappy/Click packages either of these things, and what does this mean to someone wanting to learn how to create packages?
This question should really be broken in two: "What are dynamic and static linking?", and "How does Click solve dependency issues?". The two aren't related. Here I will give attempt to answer the first question.
What are dynamic and static linking?
The source code of any software program makes use of external functions. External functions reside in libraries. When compiling the program into machine code*, the external references must somehow be linked to their definitions, i.e. to their implementation in machine code. There are two approaches to doing this. Either (a) the implementing code is "pulled in" from the libraries and added to the resulting binary, or (b) the references are left dangling and will be "pointed to" their implementation at runtime. We call (a) static linking and (b) dynamic linking.
As with any engineering decision, neither alternative is better than the other. Static linking has the advantage of not creating a dependency on the runtime environment and therefore produces more predictable binaries. This comes at the cost of duplication of code, and therefore disk, network and memory consumption. Dynamic linking enables sharing library code at runtime. It also enables upgrading libraries, for instance when a bug is found, without the need to recompile the binaries that use them. All dependants get fixed automatically. Obviously they also get broken automatically, by the bugs and incompatibilities that the new version introduces.
Leaving the pros and cons aside, dynamic linking inherently produces a more complex system. There are more moving parts and, especially, there are dependencies between the moving parts**. The question then is how to manage this complexity. For instance, how to have two versions of an application on the system, each requiring their own dependencies, among which there could be two different minor versions of one library. Because the file names of these libraries collide, this is not currently possible on Ubuntu.
One could say that every packaging system, apart from simplifying software installation, is an attempt to resolve dependency and versioning issues, while reaping the benefits of having dynamic dependencies. The Debian packaging system at the time it came about was much better at doing this than the existing ones. Since then many other solutions have come up. A notably thorough approach is taken by GNU Guix and its Guix package manager. Click is another.
*) We assume a program compiled to machine code, not one that will be interpreted or compiled into an intermediate language (e.g. Python, Java). On an abstract level these languages suffer from the very same dynamic resolution issues (
ClassNotFoundException in Java), but including them here would just muddle the explanation.
**) Note that if you stick to statically linked binaries, then there would be no runtime interdependencies. That is, no library dependencies, there are other kinds.