diff --git a/lib/prima_opentelemetry_ex/instrumentation/opentelemetry_absinthe.ex b/lib/prima_opentelemetry_ex/instrumentation/opentelemetry_absinthe.ex
index fb3512e..812a60b 100644
--- a/lib/prima_opentelemetry_ex/instrumentation/opentelemetry_absinthe.ex
+++ b/lib/prima_opentelemetry_ex/instrumentation/opentelemetry_absinthe.ex
@@ -1,27 +1,13 @@
 defmodule PrimaOpentelemetryEx.Instrumentation.OpentelemetryAbsinthe do
   @moduledoc false
-
-  cond do
-    Code.ensure_loaded?(OpentelemetryAbsinthe) ->
-      def maybe_setup do
-        if PrimaOpentelemetryEx.enabled?(:absinthe) do
-          :prima_opentelemetry_ex
-          |> Application.get_env(:graphql, [])
-          |> OpentelemetryAbsinthe.Instrumentation.setup()
-
-          nil
-        end
-      end
-
-    Code.ensure_loaded?(Absinthe) ->
-      raise """
-      Absinthe has been loaded without installing the optional opentelemetry_absinthe dependency.
-      PrimaOpentelemetryEx will not be able to instrument absinthe unless you add it.
-
-      Note: you can get rid of this warning by excluding absinthe from instrumentation with `config :prima_opentelemetry_ex, exclude: [:absinthe]`
-      """
-
-    true ->
-      def maybe_setup, do: nil
+  alias PrimaOpentelemetryEx.Instrumentation.Optional
+  require Optional
+
+  Optional.instrument Absinthe,
+    with: OpentelemetryAbsinthe,
+    feature: :absinthe do
+    :prima_opentelemetry_ex
+    |> Application.get_env(:graphql, [])
+    |> OpentelemetryAbsinthe.Instrumentation.setup()
   end
 end
diff --git a/lib/prima_opentelemetry_ex/instrumentation/opentelemetry_ecto.ex b/lib/prima_opentelemetry_ex/instrumentation/opentelemetry_ecto.ex
index 5f8c231..03d8487 100644
--- a/lib/prima_opentelemetry_ex/instrumentation/opentelemetry_ecto.ex
+++ b/lib/prima_opentelemetry_ex/instrumentation/opentelemetry_ecto.ex
@@ -1,37 +1,24 @@
 defmodule PrimaOpentelemetryEx.Instrumentation.OpentelemetryEcto do
   @moduledoc false
 
-  cond do
-    Code.ensure_loaded?(OpentelemetryEcto) ->
-      def maybe_setup do
-        if PrimaOpentelemetryEx.enabled?(:ecto) do
-          :telemetry.attach(
-            "repo-init-handler",
-            [:ecto, :repo, :init],
-            &__MODULE__.instrument_repo/4,
-            %{}
-          )
+  alias PrimaOpentelemetryEx.Instrumentation.Optional
+  require Optional
 
-          nil
-        end
-      end
-
-      def instrument_repo(_event, _measurements, metadata, _config) do
-        metadata
-        |> Map.fetch!(:opts)
-        |> Keyword.fetch!(:telemetry_prefix)
-        |> OpentelemetryEcto.setup()
-      end
-
-    Code.ensure_loaded?(Ecto) ->
-      raise """
-      Ecto has been loaded without installing the optional opentelemetry_ecto dependency.
-      PrimaOpentelemetryEx will not be able to instrument ecto unless you add it.
-
-      Note: you can get rid of this warning by excluding absinthe from instrumentation with `config :prima_opentelemetry_ex, exclude: [:ecto]`
-      """
+  Optional.instrument Ecto,
+    with: OpentelemetryEcto,
+    feature: :ecto do
+    :telemetry.attach(
+      "repo-init-handler",
+      [:ecto, :repo, :init],
+      &__MODULE__.instrument_repo/4,
+      %{}
+    )
+  end
 
-    true ->
-      def maybe_setup, do: nil
+  def instrument_repo(_event, _measurements, metadata, _config) do
+    metadata
+    |> Map.fetch!(:opts)
+    |> Keyword.fetch!(:telemetry_prefix)
+    |> OpentelemetryEcto.setup()
   end
 end
diff --git a/lib/prima_opentelemetry_ex/instrumentation/optional.ex b/lib/prima_opentelemetry_ex/instrumentation/optional.ex
new file mode 100644
index 0000000..c516d6a
--- /dev/null
+++ b/lib/prima_opentelemetry_ex/instrumentation/optional.ex
@@ -0,0 +1,56 @@
+defmodule PrimaOpentelemetryEx.Instrumentation.Optional do
+  @moduledoc false
+
+  @doc """
+    Creates a maybe_setup function that instruments a library using an optional instrumentation library.
+    
+    Executes the `do` block if:
+      * `to_instrument` is available
+      * `instrumenting_library` is available
+      * `feature_name` is not in the prima_opentelemetry_ex exclude list
+    
+    * `to_instrument`: the module that will be instrumented(eg. `Ecto`)
+    * `instrumenting_library`: the module that will be used to instrument(eg. `OpentelemetryEcto`)
+    * `feature_name`: atom specifying the name of the feature. Typically the name of the to_instrument library in atom format(eg. `:ecto`)
+  """
+  defmacro instrument(
+             to_instrument,
+             [
+               with: instrumenting_library,
+               feature: feature_name
+             ],
+             do: setup_instrumentation
+           ) do
+    quote do
+      cond do
+        # First check if the feature is enabled on compile time
+        not PrimaOpentelemetryEx.enabled?(unquote(feature_name)) ->
+          def maybe_setup, do: nil
+
+        Code.ensure_loaded?(unquote(instrumenting_library)) ->
+          def maybe_setup do
+            # Runtime check to make sure the feature is enabled on runtime time
+            if PrimaOpentelemetryEx.enabled?(unquote(feature_name)) do
+              unquote(setup_instrumentation)
+              nil
+            end
+          end
+
+        Code.ensure_loaded?(unquote(to_instrument)) ->
+          raise """
+          #{unquote(to_instrument)} has been loaded without installing the optional #{
+            unquote(instrumenting_library)
+          } dependency.
+          PrimaOpentelemetryEx will not be able to instrument absinthe unless you add it.
+
+          Note: you can get rid of this warning by excluding absinthe from instrumentation with `config :prima_opentelemetry_ex, exclude: [:#{
+            unquote(feature_name)
+          }]`
+          """
+
+        true ->
+          def maybe_setup, do: nil
+      end
+    end
+  end
+end