You could go the other way using :source-paths
in project.clj
. E.g.
;; project.clj
(defproject project "0.1.0-SNAPSHOT"
...
:profiles
{:dev [:project/dev :profiles/dev]
:profiles/dev {}
:project/dev {:source-paths ["dev/src"]
:resource-paths ["dev/resources"]}})
;; dev/src/project/external/service.clj
(ns project.external.service
(:require [integrant.core :as ig]))
;; Public interface
(defprotocol ExternalService
(get-smth [this arg]))
(defrecord Boundary [config]
ExternalService
(get-smth [_ arg] (prn arg))
(defmethod ig/init-key :project.external/service [_ config]
(->Boundary config))
;; src/project/external/service.clj
(ns project.external.service
(:require [integrant.core :as ig]))
;; Public interface
(defprotocol ExternalService
(get-smth [this arg]))
(defrecord Boundary [config]
ExternalService
(get-smth [_ arg] (implementation-for arg))
(defmethod ig/init-key :project.external/service [_ config]
(->Boundary config))
I'd be tempted to write two keys, :project.external.service/fake
and :project.external.service/live
, both inheriting from :project.external/service
. Then just replace the live key with the fake one in development.