That's a very good question. Unfortunately, I've no definitive answer for you. I can only detail the main strengths and weaknesses of the different approaches.
1. Standalone plugins
This is the dominant approach. Code from other plugins are copied.
- The end-user won't have to install several plugins
- It's the friendliest approach with the plugin managers everybody use
- Maintainability is catastrophic: you won't profit from bug fixes, performance improvements, or even added features
- If you're serious about licences, this could get ugly
2. Plugins that depend on other plugins
Very few plugins follow this approach. End-users have to install the plugins we depend upon. Dare I say this is the most professional one.
- Maintainability -- it's the exact opposite of the approach 1
- Copyright: it's easier to depend on plugins with different licences without having to use a licence different from the one we would have chosen, or to mix licences within a same plugin, or to violate original licences by changing it without the initial author knowledge/explicit authorization
- Installing a plugin that depends on others may become very complicated without assistance: see lh-cpp requirements for instance. Without VAM or vim-flavor, this is a nightmare.
- Very few people use plugin managers that understand dependencies => this is a nightmare for maintainers to track dependencies (what if a plugin we depend upon introduce a new dependency?), and for end-users to know exactly what is required by each plugin, and to know when a plugin introduce a new dependency...
- If we depend on a specific version of a plugin, this could get ugly -- see the dependencies issues in ruby world. vim-flavor helps a little here
My choice on the subject
I'm maintaining something like almost 20 different plugins. A long time ago I've chosen to define a plugin library that other plugins depend upon. This library contains a lot of things. I definitively not regret to have made this choice. Thanks to that I've a efficient solution to debug and log what happens in my plugins, many list related functions that should have been defined in vim, and so on. And I don't maintain it several times, but only once. When I've added DbC for lh-tags, I've been able to use it immediately in build-tools-wrappers, where I've introduced new assertions that were available in lh-tags without having to synchronize any file.
Regarding installation, every time somebody asks about plugin managers, I explain why I prefer VAM or vim-flavor: they've understood the importance of dependencies. Nobody would use a yum/dnf/apt-get that don't handle dependencies, and yet this is what most do in vim world. As trendy plugin managers don't understand dependencies, plugin avoid to have dependencies, as thus plugin managers don't need to support dependencies, and so on. This is a vicious circle.
Regarding CI, and more precisely tests, I do CI on travis. I've cooked my own solution that use vim-flavor + vimrunner + rpsec + my UT framework. vim-flavor takes care of dependencies on travis.
You could use your plugin start-up to automatically fetch another plugin, but honestly, don't. What if 3 other plugins do the same thing with the same dependency? The end-user would see the dependency being fetched and activated at least 3 times. Explicitly or implicitly, dependencies should being handled by the plugin manager used by the end-user.
The real question is: what burden are you ready to put on your end-users?