= Improving Development Startup Time
Alex Miller
2020-03-12
:type: guides
:toc: macro
:icons: font

ifdef::env-github,env-browser[:outfilesuffix: .adoc]

Did you know:

* Most libraries are distributed as source and you compile them over and over
* You can compile namespaces explicitly using https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/compile[`compile`]
* Namespace compilation is transitive
* `compile` writes these files to disk and `require` will use them
* You can use `compile` on the namespace you load when you start development, or on your `user.clj`, or on the main namespace you run as a server to improve your startup time

The `compile` function takes a namespace symbol and compiles that namespace and all the namespaces it requires into https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/%2Acompile-path%2A[`\*compile-path*`] (which defaults to `classes`). That directory must exist and be on your classpath:

[source,clojure]
----
(compile 'my.namespace)    ;; writes .class files to *compile-path*
----

Subsequently, when any of those compiled namespaces are required, the class file will be loaded, rather than the original `.clj` file. If a source file is updated (and thus newer), it will be loaded instead. Periodically, you will need to re-compile to account for new dependencies or changing code.

Note that compilation is a side effect of loading, so has no effect on already loaded namespaces. In particular, one special case is the `user.clj` file, which is loaded automatically by the Clojure runtime. If you are using a `user.clj` in dev (or need to compile namespaces that are already compiled), you can do so by forcing a reload while compiling:

[source,clojure]
----
(binding [*compile-files* true] ;; compile during load
  (require 'user :reload-all))  ;; reload this and all transitively loaded namespaces
----

That's it! This technique can substantially reduce your startup time during development, particularly as the number of dependencies you load increases.

== Using an alias for development

In a deps.edn project, you should create a development alias (like `:dev`) that includes the `classes` directory:

[source,clojure]
----
{:deps { ... }
 :aliases
 {:dev {:extra-paths ["classes"]}}}
----

You will also need to ensure the `classes` directory exists. It may be useful to include the empty `classes` directory as part of your version controlled project structure (make sure to ignore and NOT include the compiled .class files).

You can then start your REPL with the alias and `compile` to populate your `classes` directory and start seeing the benefits:

[source,shell]
----
$ clj -A:dev
Clojure 1.10.1
user=> (compile 'my.namespace)
----

== Incorporating user.clj into your development alias

If you want to use an automatically loaded user.clj, you should incorporate that into your dev alias by adding a source path `dev`:

[source,clojure]
----
{:deps { ... }
 :aliases
 {:dev {:extra-paths ["dev" "classes"]}}}
----

And then create `dev/user.clj`:

[source,clojure]
----
(ns user
  (:require ... ))

;; dev-only functions, etc
----

Remember to use the modified compilation process for user.clj:

[source,shell]
----
$ clj -A:dev
Clojure 1.10.1
user=> (binding [*compile-files* true] (require 'user :reload-all))
----
