Thanks for bringing up this important topic. Over the years I flip-flopped between [library/objectname] and [declare] a few times, for reasons which I'll try to explain.
The first occasion where I needed [library/objectname] was when [pow~] in pd core had it's inlets swapped between pd versions, while [cyclone/pow~] retained the old inlet order and could be used for backward compatibility.
When Pd-extended stopped loading libraries by default I started using [library/objectname] for externals from all Pd-extended libraries, which was recommended practice.
Later, I came across a few cases where externals moved from one lib to another. For example, [flatspace/knob] became [flatgui/knob]. I feared further library reorganizations and abandoned the rigid [library/objectname] format. Now I started to appreciate the flexibility provided by [declare]. I used declarations like [declare -stdlib flatspace/knob] and [declare -stdlib flatgui/knob] in top level patches to make projects compatible across Pd-extended versions. Also, I use [declare] for binaries which are included in project directories: for my homebrew binaries, or selected Pd-extended binaries in projects which should run with vanilla Pd. It is much easier to change or add a few declarations in the top level patch than changing library names in all object instances in a large project.
But [declare] only adds search paths and it doesn't protect against name clash. I came across [bsaylor/svf~] which has different number of inlets and outlets than [cyclone/svf~]. Without library name, [svf~] instantiates the cyclone version. But when [bsaylor/svf~] was instantiated earlier in the Pd session, or with [import bsaylor] in the patch, the bsaylor version may be instantiated. Without namespace specification Pd seems to use the version that was loaded first within a Pd session.
I realized that instantiating without library namespaces is a gambling. And this does not only apply to binary executables, but to abstractions as well. Imagine you share a Pd project including abstractions and binaries, and you publish a new version of the project, where bugs in externals were fixed or new features were added to abstractions. Users may want to compare versions, and load the old version first in a Pd session. Even when they close the old version of the project before loading the new version, the old executables and abstraction definitions are still in Pd's memory, and Pd will not load the new ones unless they were prefixed by a different namespace, or until Pd is restarted. So, the new project version may seem to still contain the old bugs!
The current solution for such issues is systematic use of lib names and relative paths in object or abstraction instances. For example, include abstractions in a subdirectory named 'abstractions', and instantiate as [abstractions/objectname]. Pd will then load the abstraction from the subdirectory in the project, even when many projects have an 'abstractions' subdirectory containing an abstraction with the same name. This tedious and inflexible approach will only work when directory structures don't change. A project or library structure must be well organized right from the start. If something changes in the structure, you may need to find all instances of a class in your patch(es) and redefine them. Rigid directory structures, and the prospect of minor changes causing heaps of maintenance work, can be an obstacle for future innovations in a project, or in Pd-extended as a whole.