Some time after first hearing about it, then ignoring it, then seeing it mentioned everywhere, then understanding why it was useful, and then wanting to use it, I finally switched to the straight.el package manager. The primary reason was to better handle packages directly loaded from Git. The secondary reason was the attraction of a reproducible configuration.

The immediate impetus for the switch was finding a pair of packages I wanted to use that aren’t available on MELPA. I’ve encountered such packages before, but this time I was predisposed towards making the switch.

I was already loading packages with use-package, so, once I had straight.el installed, I replaced :ensure t with :straight t. That left about a dozen packages being loaded from cloned Git repositories with a homegrown :local keyword in the use-package form. (I have a vague memory of there being a way to do that directly with use-package, but if such a thing does exist, I must not have not known about it at the time. use-package’s own :load-path is similar but not the same.) Before I updated those, I made sure I didn’t have any uncommitted changes.

Once every :straight t was in place, I restarted Emacs a few times, incrementally correcting the order in which the packages were loaded as errors appeared. I had a bad habit of using nested use-package forms to conditionally load packages, but straight.el requires that reloading your init-file must have the effect of running all of the same straight.el-related functions again, so I updated all of those cases to use :if or :after instead of nesting. That seems like a more sensible approach in any case.

It took a few attempts to understand how to create the lockfile in my version-controlled Emacs directory rather than the default application data directory. I ultimately needed to set straight-profiles before bootstrapping straight.el (shiv/local-dir and shiv/my-file being unrelated helper functions that produce absolute paths):

Emacs Lisp(setf straight-build-dir (shiv/local-dir "straight")
      straight-profiles `((nil . ,(shiv/my-file "straight.lockfile.el"))))

; normal straight.el bootstrap process follows
(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
         'silent 'inhibit-cookies)
      (goto-char (point-max))
  (load bootstrap-file nil 'nomessage))

It took 20–25 minutes to build all the packages in my configuration from source, which I had to do twice because of a misunderstanding, and it took another two minutes to update the cache before producing a lockfile, which I also had to do multiple times as I searched for the right way to change the path.

There were a few packages with less simple requirements, which I had heretofore been loading in a more ad hoc fashion. Even then, the most complex—capnp-mode for Cap’n Proto files—took very little to configure correctly. Cloning the repository, narrowing it down to the Emacs Lisp code in a subdirectory, loading it, and associating the mode with the file extension is trivial:

Emacs Lisp(use-package capnp-mode
  :straight (capnp-mode :host github :repo "capnproto/capnproto" :files ("highlighting/emacs/*.el"))
  :mode "\\.capnp\\'")

On the whole, I’m very pleased to have been able to remove significant amounts of custom code and create a more reliable configuration in the bargain. I’m disappointed that pinning packages to tags isn’t possible yet, but recording Git revisions in the lockfile somewhat mitigates the omission.