Whatever we choose to call them, there are three distinct concepts:
- Reusable code:
- provides mechanism for other code to load it: YES
- can provide global runtime configuration: NO
- meaningful to “run it” on its own: NO
- Top-level code:
- provides mechanism for other code to load it: NO
- can provide global runtime configuration: YES
- meaningful to “run it” on its own: YES
- Either of the above.
Reusable code cannot provide global runtime configuration because if it does then different units of reusable code can provide conflicting configuration. We already call 1 a “package” and we’re going to keep on doing that. We can either call 2 “project” and call 3 “project or package” or we can call 3 “project” and call 2 “top-level project”. I’m not sure which is better, but trying to argue that there’s no distinction is not especially helpful since the distinction is real. At the same time, keeping the distinction as small as possible is a good idea. If you have a project and you want to turn it into package, it should largely be a simple matter of deleting the global configuration and providing an appropriate entry-point for loading code.
It might also make sense to allow other sensible combinations of these three yes/no choices, but I think having terminology for the most common patterns is still helpful. One idea would be having “targets” and associating global runtime configuration with targets. Top-level code would then simply be a project with a “run” target. Other targets could include “test” in various incarnations. Not entirely sure how loading code fits in.