Loose coupling is useful, among other things, for practical system/software extensibility; or, in other words, tight coupling complicates it. It's the recent investigation of XMPP client implementations (with some of the issues arising because of restrictive APIs) that made me to consider it this time, though the problem is pervasive. XMPP looks like a nice problem example, since there is a small core, and a set of extensions – all specified and sane, with new ones appearing occasionally, but some of them altering some aspects of the core behaviour (e.g., cancelling resource binding on stream resumption). In its implementation, it would be nice to define the core once, and only extend it with separate modules implementing extensions then; likewise with multi-protocol clients that may use it.
Separating a system into smaller reusable tools with defined responsibilities and APIs (applications, libraries, microservices, actors, generic abstractions available in a given language, etc) helps to some extent on its own, but it's far from a complete solution; as plugin and scripting mechanisms, those still depend on (and are restricted by) resulting APIs, and unforeseen uses are complicated without adjusting the core.
Emacs and Prosody are good examples of highly extensible programs. They provide various ways to customise their behaviour, including common functions and configuration by setting variables, but what distinguishes them is that they allow new modules/packages/modes to alter existing control flows (albeit implicitly) by making use of hooks/advices/events, employing event-driven architecture. libpurple does something similar with signals, but to a lesser extent, and still relying on restrictive APIs and conventions.
In the XMPP example, the core flow (as in RFC 6120) is defined in steps, and it would be sufficient for alteration if those steps (or merely RFC sections) were defined as nodes in an alterable control flow graph.
As for implementation, there are technologies that may be useful, or have similarities and may be used for inspiration: DRAKON, π-calculus (and other process calculi), perhaps dynamically typed languages, RDF graphs. It's almost trivial in lisps, not hard in deptyped languages, but doable in Haskell as well.
Although such an approach may also become overly verbose and complicated, so it's hard to be any certain that it would be practical.