diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt
index 3aa653eee09362c1473d8a3962a2ca58fea2f0cf..3600b318c5c8b7b0739c4732cf842e9ea9ab57a6 100644
--- a/apps/CMakeLists.txt
+++ b/apps/CMakeLists.txt
@@ -1,4 +1,4 @@
 add_executable(compare_convergence
     compare_convergence.cpp
 )
-target_link_libraries(compare_convergence optimization_lib cma-es)
+target_link_libraries(compare_convergence optimization_lib)
diff --git a/optimization/CMakeLists.txt b/optimization/CMakeLists.txt
index 4f3b6e25410928c2f2ad8c279cb22d586251db37..a35d7669250d9b3b43088e11c261de890644f49b 100644
--- a/optimization/CMakeLists.txt
+++ b/optimization/CMakeLists.txt
@@ -4,7 +4,7 @@ target_link_libraries(optimization_lib INTERFACE shared_settings Eigen3::Eigen)
 
 if (VISUALIZE)
     target_compile_definitions(optimization_lib INTERFACE VISUALIZE)
-    target_link_libraries(optimization_lib INTERFACE json)
 endif()
+target_link_libraries(optimization_lib INTERFACE cma-es json)
 
 add_subdirectory(optimizers)
diff --git a/optimization/optimizers/gradient_descent/CMakeLists.txt b/optimization/optimizers/gradient_descent/CMakeLists.txt
index bb3b52e461bcfa71127efe678e0f49bdf1473c63..8684fbfa59edfc706ac643de7e556acfa8e0f4ac 100644
--- a/optimization/optimizers/gradient_descent/CMakeLists.txt
+++ b/optimization/optimizers/gradient_descent/CMakeLists.txt
@@ -3,7 +3,5 @@ add_executable(gradient_descent
 )
 target_link_libraries(gradient_descent optimization_lib clara)
 
-if (VISUALIZE)
-    make_plot_target(gradient_descent 2d ARGS -d 2 -l 0.0015 -n 10000)
-    make_plot_target(gradient_descent 10d ARGS -d 10 -l 0.0005 -n 10000)
-endif()
+make_plot_target(gradient_descent 2d ARGS -d 2 -l 0.0015 -n 10000)
+make_plot_target(gradient_descent 10d ARGS -d 10 -l 0.0005 -n 10000)
diff --git a/optimization/optimizers/gradient_descent/logs/everything.h b/optimization/optimizers/gradient_descent/logs/everything.h
index c9be4ec064dcfd3c3d33e8ac882996090ae5db07..8eaf55772a7043ca1f1834162003d8ec28c8c5de 100644
--- a/optimization/optimizers/gradient_descent/logs/everything.h
+++ b/optimization/optimizers/gradient_descent/logs/everything.h
@@ -1,15 +1,12 @@
 #ifndef OPTIMIZATION_GRADIENT_DESCENT_LOGS_EVERYTHING_H
 #define OPTIMIZATION_GRADIENT_DESCENT_LOGS_EVERYTHING_H
 
+#include "objectives/samples.h"
+#include "objectives/samples_vis.h"
+#include "utils/eigen_json.h"
 #include "utils/vector.h"
 #include "utils/vis_only.h"
-#include "objectives/samples.h"
-
-#ifdef VISUALIZE
 #include <vector>
-#include "json.hpp"
-#include "objectives/samples_vis.h"
-#endif
 
 namespace optimization {
 
@@ -19,39 +16,20 @@ namespace optimization {
 template <int32_t N>
 struct GradientDescentLogEverything {
     template <typename Objective>
-    void initialize(Objective const&) VIS_ONLY_METHOD;
+    void initialize(Objective const&) { objective_name = Objective::name; }
     void push_back(
         VectorNs<N> const& point,
         Scalar value,
         VectorNs<N> const& gradient
-    ) VIS_ONLY_METHOD;
-    void clear() VIS_ONLY_METHOD;
+    ) {
+        samples.emplace_back(point, value, gradient);
+    }
+    void clear();
 
-    #ifdef VISUALIZE
     std::string objective_name;
     std::vector<GradientSample<VectorNs<N>>> samples;
-    #endif
 };
 
-#ifdef VISUALIZE
-
-//..................................................................................................
-template <int32_t N>
-template <typename Objective>
-void GradientDescentLogEverything<N>::initialize(Objective const&) {
-    objective_name = Objective::name;
-}
-
-//..................................................................................................
-template <int32_t N>
-void GradientDescentLogEverything<N>::push_back(
-    VectorNs<N> const& point,
-    Scalar value,
-    VectorNs<N> const& gradient
-) {
-    samples.emplace_back(point, value, gradient);
-}
-
 //--------------------------------------------------------------------------------------------------
 template <int32_t N>
 void to_json(nlohmann::json& j, GradientDescentLogEverything<N> const& log) {
@@ -62,8 +40,6 @@ void to_json(nlohmann::json& j, GradientDescentLogEverything<N> const& log) {
     };
 }
 
-#endif
-
 }
 
 #endif
diff --git a/optimization/optimizers/gradient_descent/logs/nothing.h b/optimization/optimizers/gradient_descent/logs/nothing.h
index 3178cf774ed49c5f51f53f1d9542debdd8f19c50..1260357e51a9cf6779fc0d384cb02fbac5b94395 100644
--- a/optimization/optimizers/gradient_descent/logs/nothing.h
+++ b/optimization/optimizers/gradient_descent/logs/nothing.h
@@ -11,7 +11,7 @@ struct GradientDescentLogNothing {
     void initialize(Objective const&) {}
 
     template <int32_t N>
-    void push_back(VectorNs<N> const& point, Scalar value, VectorNs<N> const& gradient) {}
+    void push_back(VectorNs<N> const&, Scalar, VectorNs<N> const&) {}
 };
 
 }
diff --git a/optimization/optimizers/gradient_descent/main.cpp b/optimization/optimizers/gradient_descent/main.cpp
index 1032b09750bf49bb4ab23f3ca8fd739ad8818ca9..b71f58896ae4bdbe003c49c7587eac5cdf1a2059 100644
--- a/optimization/optimizers/gradient_descent/main.cpp
+++ b/optimization/optimizers/gradient_descent/main.cpp
@@ -1,17 +1,14 @@
 #include "clara.hpp"
 #include "gradient_descent.h"
-#include "objectives/paraboloid.h"
-#include "objectives/rosenbrock.h"
 #include "logs/everything.h"
-#include <iostream>
-
-#ifdef VISUALIZE
 #include "logs/everything_vis.h"
+#include "objectives/paraboloid.h"
+#include "objectives/rosenbrock.h"
 #include "utils/eigen_json.h"
 #include <fstream>
+#include <iostream>
 
 using json = nlohmann::json;
-#endif
 
 using namespace optimization;
 
@@ -55,25 +52,32 @@ int main(int const argc, char const** argv) {
     Objective objective;
     objective.dim() = dim;
 
-    using Log = GradientDescentLogEverything<-1>;
-    GradientDescent<-1, Log> optimizer(learning_rate, max_evaluations, gradient_threshold);
-    VectorXs minimum = optimizer.optimize(objective, initial_point);
-    std::cout << "n evaluations: " << optimizer.n_evaluations() << '\n';
-    std::cout << "final point: " << minimum << '\n';
+    if (log_file_path.empty() && vis_file_path.empty()) {
+        // If we're not saving data, use a lean optimizer.
+        // TODO: Find a way to deduplicate code between these branches.
+        GradientDescent<-1> optimizer(learning_rate, max_evaluations, gradient_threshold);
+        VectorXs minimum = optimizer.optimize(objective, initial_point);
+        std::cout << "n evaluations: " << optimizer.n_evaluations() << '\n';
+        std::cout << "final point: " << minimum << '\n';
+    } else {
+        using Log = GradientDescentLogEverything<-1>;
+        GradientDescent<-1, Log> optimizer(learning_rate, max_evaluations, gradient_threshold);
+        VectorXs minimum = optimizer.optimize(objective, initial_point);
+        std::cout << "n evaluations: " << optimizer.n_evaluations() << '\n';
+        std::cout << "final point: " << minimum << '\n';
 
-    #ifdef VISUALIZE
-    if (!log_file_path.empty()) {
-        json data = optimizer;
-        std::ofstream log_file(log_file_path);
-        log_file << data.dump(4) << '\n';
-    }
+        if (!log_file_path.empty()) {
+            json data = optimizer;
+            std::ofstream log_file(log_file_path);
+            log_file << data.dump(4) << '\n';
+        }
 
-    if (!vis_file_path.empty()) {
-        json data = GradientDescentVis<-1>{optimizer};
-        std::ofstream vis_file(vis_file_path);
-        vis_file << data.dump(4) << '\n';
+        if (!vis_file_path.empty()) {
+            json data = GradientDescentVis<-1>{optimizer};
+            std::ofstream vis_file(vis_file_path);
+            vis_file << data.dump(4) << '\n';
+        }
     }
-    #endif
 
     return 0;
 }