The difference is one of runtime vs compile time.
uproject/uplugin entries are used by the engine to determine if and when to load modules, at runtime.
ExtraModuleNames in the target file tells UBT that it needs to compile the module. As I noted in my article you linked, if you have a dependency chain to your module from another that is already specified here, then adding it explicitly won’t do anything.
Essentially yes, to use code you need to specify the module dependency, but also will need the symbol that you want to use to have been exported via the MODULENAME_API. Under the hood though, it’s complex.
Virtual method calls, for example, don’t require anything to have been exported. It’s also platform dependent - the UE4 build system is attempting to wrap multiple platforms that behave differently in this respect a one consistent way. If you want to know more details, search outside of the UE4 context, for things like dllimport/dllexport.
The module class derives from IModuleInterface, which has some virtual methods you can override to do things when the module is loaded/unloaded. FDefaultModuleImpl is just the one you can use if you don’t have any need to define your own module class with specific behaviour. Here’s an example of a non-default module class.