<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.7.0">Jekyll</generator><link href="https://blog.jeaye.com//feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.jeaye.com//" rel="alternate" type="text/html" /><updated>2020-11-03T14:26:55-08:00</updated><id>https://blog.jeaye.com//</id><entry><title type="html">Removing IME: Upgrading System76 firmware on Arch</title><link href="https://blog.jeaye.com//2018/04/11/system76-firmware/" rel="alternate" type="text/html" title="Removing IME: Upgrading System76 firmware on Arch" /><published>2018-04-11T00:00:00-07:00</published><updated>2018-04-11T00:00:00-07:00</updated><id>https://blog.jeaye.com//2018/04/11/system76-firmware</id><content type="html" xml:base="https://blog.jeaye.com//2018/04/11/system76-firmware/">&lt;p&gt;In the wake of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Intel_Management_Engine&quot;&gt;Intel Management
Engine&lt;/a&gt; security
revelations, &lt;a href=&quot;https://system76.com/&quot;&gt;System76&lt;/a&gt; developed a firmware upgrade for
all of its machines and released an &lt;a href=&quot;http://blog.system76.com/post/168050597573/system76-me-firmware-updates-plan&quot;&gt;update
plan&lt;/a&gt;
in November 2017. In February 2018, owners of the &lt;a href=&quot;https://system76.com/laptops/oryx&quot;&gt;Oryx
Pro&lt;/a&gt; were informed that the firmware update
was available through System76’s open source &lt;a href=&quot;https://github.com/system76/firmware-update&quot;&gt;firmware
updater&lt;/a&gt;. For anyone not on
System76’s &lt;a href=&quot;https://system76.com/pop&quot;&gt;Pop!_OS&lt;/a&gt; or similar Debian-based distros,
this firmware updater probably &lt;em&gt;didn’t do anything&lt;/em&gt;. After waiting patiently for
a couple of months for more updates and not seeing any fixes, I dug into how I
could get things going. Herein lies the easiest way I found.&lt;/p&gt;

&lt;h3 id=&quot;before-proceeding&quot;&gt;Before proceeding&lt;/h3&gt;
&lt;p&gt;To start with, try the firmware update normally, as recommended by the &lt;a href=&quot;http://support.system76.com/articles/laptop-firmware/&quot;&gt;System76
docs&lt;/a&gt;. The approach I
took is pretty hacky, but it was the only thing which worked for my setup.
Ideally, you can take a more trodden route.&lt;/p&gt;

&lt;h3 id=&quot;silent-errors-when-running&quot;&gt;Silent errors when running&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://aur.archlinux.org/packages/system76-driver/&quot;&gt;system76-driver AUR
package&lt;/a&gt; provides a systemd
service which just runs the &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware&lt;/code&gt; command to check for firmware
updates. In my case, when I ran it, I saw something like the following:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ system76-firmware
2018-04-08 10:48:51,895  INFO  Verified manifest signature...
2018-04-08 10:48:51,895  INFO  Fetching f7cd3816401c6ab1cd2f0a83285a56ee432a9736b707870fb7aeb34c2750bcefc2adcf0f83952696eb688b9768e93f68 with cache /var/cache/system76-firmware
2018-04-08 10:48:51,897  INFO  Fetching e4206477b3f5bad09d54363a78ae79e2916127399e9725c3b9d77bf229c25c293111926d841c1c05b186a96c0963f6ff with cache /var/cache/system76-firmware
2018-04-08 10:48:51,933  INFO  Fetching ec0b0b475412acde6b2b9a05647a64f48beaa5baea298e8801ce1a34bbddcdada5fc2d8025b2e6f07d9802c384a01e7c with cache /var/cache/system76-firmware
2018-04-08 10:48:52,262  INFO  Verified manifest signature...
2018-04-08 10:48:52,263  INFO  Fetching f7cd3816401c6ab1cd2f0a83285a56ee432a9736b707870fb7aeb34c2750bcefc2adcf0f83952696eb688b9768e93f68 with cache /var/cache/system76-firmware
2018-04-08 10:48:52,265  INFO  Fetching e4206477b3f5bad09d54363a78ae79e2916127399e9725c3b9d77bf229c25c293111926d841c1c05b186a96c0963f6ff with cache /var/cache/system76-firmware
2018-04-08 10:48:52,299  INFO  Fetching ec0b0b475412acde6b2b9a05647a64f48beaa5baea298e8801ce1a34bbddcdada5fc2d8025b2e6f07d9802c384a01e7c with cache /var/cache/system76-firmware
$ echo $?
0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;No errors, no warnings, a clean exit code, but it didn’t update my firmware or
give me any insight into how or when that was going to happen. This is how
things were since the &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware&lt;/code&gt; command was added to the AUR package.&lt;/p&gt;

&lt;h3 id=&quot;issues-with-the-display&quot;&gt;Issues with the display&lt;/h3&gt;
&lt;p&gt;When deciding to dig into what was going on, the first thing I did was step
through the program with &lt;a href=&quot;https://docs.python.org/2/library/pdb.html&quot;&gt;pdb&lt;/a&gt;. From
there, it became clear that &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware&lt;/code&gt; is trying to show a modal, but
can’t find the display name, so it &lt;a href=&quot;https://github.com/pop-os/system76-driver/blob/master_artful/system76driver/firmware.py#L467&quot;&gt;exits
silently&lt;/a&gt;.
It’s reading the display name from &lt;code class=&quot;highlighter-rouge&quot;&gt;who&lt;/code&gt;, which is odd, since I’d think it’d
just check &lt;code class=&quot;highlighter-rouge&quot;&gt;DISPLAY&lt;/code&gt;, so I edited the source to just return &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;:0&quot;&lt;/code&gt;, which is the
value of &lt;code class=&quot;highlighter-rouge&quot;&gt;echo $DISPLAY&lt;/code&gt; in my X session. This could be because I’m running
&lt;a href=&quot;https://i3wm.org/&quot;&gt;i3-wm&lt;/a&gt; and not GNOME or KDE, but it’s an assumption made on
System76’s part either way.&lt;/p&gt;

&lt;p&gt;For the file: &lt;code class=&quot;highlighter-rouge&quot;&gt;/usr/lib/python3.6/site-packages/system76driver/firmware.py&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;diff --git a/firmware.py b/firmware.py
index 95bafe1..90c60f9 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/firmware.py
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/firmware.py
&lt;/span&gt;&lt;span class=&quot;gu&quot;&gt;@@ -444,6 +444,7 @@ def get_user_session():
&lt;/span&gt;                     &quot;who | awk -v vt=tty$(fgconsole) '$0 ~ vt {print $5}'&quot;,
                     shell=True
                 ).decode('utf-8').rstrip('\n').lstrip('(').rstrip(')')
&lt;span class=&quot;gi&quot;&gt;+    display_name = &quot;:0&quot; # XXX: Hack
&lt;/span&gt; 
     user_pid = subprocess.check_output(
                     &quot;who -u | awk -v vt=tty$(fgconsole) '$0 ~ vt {print $6}'&quot;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;issues-with-efibootmgr&quot;&gt;Issues with efibootmgr&lt;/h3&gt;
&lt;p&gt;After that one line fix, running &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware&lt;/code&gt; resulted in a modal! I
could choose to close the modal or click a button to install the firmware. After
trying to install, another modal shows up with the procedure instructions, as
they’re also shown on the &lt;a href=&quot;http://support.system76.com/articles/laptop-firmware/&quot;&gt;System76 support
page&lt;/a&gt;, and a button to
restart into the firmware updater. Restart!&lt;/p&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;p&gt;Back into GRUB, then into Arch, with no firmware updates. Damn. So I dug more
into the source for, hopefully, another one liner. There’s a bash script, as part
of the Python source, called
&lt;a href=&quot;https://github.com/pop-os/system76-driver/blob/master_artful/system76driver/firmware.py#L206&quot;&gt;FIRMWARE_SET_NEXT_BOOT&lt;/a&gt;.
It looks like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;EFIDEV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;findmnt &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; /boot/efi &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; SOURCE&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;EFINAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EFIDEV&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;EFISYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;readlink&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/sys/class/block/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EFINAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;EFIPART&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EFISYS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/partition&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DISKSYS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dirname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EFISYS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DISKNAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;basename&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DISKSYS&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DISKDEV&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/dev/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DISKNAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\e&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[1mCreating Boot1776 on &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DISKDEV&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EFIPART&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\e&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[0m&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
efibootmgr &lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 1776 &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true
&lt;/span&gt;efibootmgr &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 1776 &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DISKDEV&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;EFIPART&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\\system76-firmware-update\\boot.efi'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;system76-firmware-update&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\e&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[1mSetting BootNext to 1776&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\e&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[0m&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
efibootmgr &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1776

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\e&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[1mInstalled system76-firmware-update&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\e&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;[0m&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
efibootmgr &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is what’s trying to make the next boot go into the updater’s EFI file
instead of the normal boot order. So, already, there are some assumptions made.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The user is booting with EFI&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi&lt;/code&gt; is mounted&lt;/li&gt;
  &lt;li&gt;Writing the System76 updater EFI to &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi/system76-firmware-update/&lt;/code&gt; is useful&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;efibootmgr -C&lt;/code&gt; is valid&lt;/li&gt;
  &lt;li&gt;Errors don’t matter. They do though, so it should be using &lt;a href=&quot;https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/&quot;&gt;safer
settings&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For all but the first of these assumptions, on my Arch setup, System76 is
guessing incorrectly. Yes, I’m booting with EFI, but &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi&lt;/code&gt; isn’t always
mounted. Furthermore, the &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware-update&lt;/code&gt; directory should be
installed into &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi/EFI&lt;/code&gt; if it wants to actually be useful. Finally,
according to the &lt;code class=&quot;highlighter-rouge&quot;&gt;efibootmgr&lt;/code&gt; man page, &lt;code class=&quot;highlighter-rouge&quot;&gt;-c&lt;/code&gt; should be used to create boot
entries. &lt;code class=&quot;highlighter-rouge&quot;&gt;-C&lt;/code&gt; doesn’t exist. In your &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware&lt;/code&gt; output, you’ll probably
see something like this (from &lt;code class=&quot;highlighter-rouge&quot;&gt;efibootmgr&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Could not prepare Boot variable: No such file or directory
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, you might try the following, in order to get your restarts to bring you into
the updater:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Patch the &lt;a href=&quot;https://github.com/pop-os/system76-driver/blob/master_artful/system76driver/firmware.py#L224&quot;&gt;bash script&lt;/a&gt; to use &lt;code class=&quot;highlighter-rouge&quot;&gt;-c&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;-C&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;efibootmgr&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Mount &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi&lt;/code&gt; and &lt;em&gt;then&lt;/em&gt; run &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware&lt;/code&gt; (then see if it installs properly and try rebooting)&lt;/li&gt;
  &lt;li&gt;Move &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi/system76-firmware-update&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi/EFI/system76-firmware-update&lt;/code&gt; and then run &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware&lt;/code&gt; again, to see if &lt;code class=&quot;highlighter-rouge&quot;&gt;efibootmgr -c&lt;/code&gt; picks it up and try your restart&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I was able to get &lt;code class=&quot;highlighter-rouge&quot;&gt;efibootmgr&lt;/code&gt; to finally run without error, but a restart still
just brought me to GRUB. So, I took a more manual route.&lt;/p&gt;

&lt;h3 id=&quot;before-proceeding-get-rescatux&quot;&gt;Before proceeding: get Rescatux&lt;/h3&gt;
&lt;p&gt;Very importantly, make sure you have a USB or CD/DVD with a burned image of
&lt;a href=&quot;https://www.supergrubdisk.org/rescatux/&quot;&gt;Rescatux&lt;/a&gt;. Rescatux is an amazing 20MB
rescue boot image which can boot into most anything. This is not a precaution;
if you follow these steps, you &lt;em&gt;will&lt;/em&gt; need Rescatux, so burn it now.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Download it &lt;a href=&quot;https://www.supergrubdisk.org/category/download/rescatuxdownloads/rescatux-beta/&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Burn with &lt;code class=&quot;highlighter-rouge&quot;&gt;dd&lt;/code&gt; or whatever you prefer&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;manually-loading-the-updater-efi&quot;&gt;Manually loading the updater EFI&lt;/h3&gt;
&lt;p&gt;My idea was simple: just add a GRUB entry for the firmware updater, since I can
so reliably make it into GRUB. The catch is that the firmware updater, thinking
that it was booted from the temporary boot path made by the bash script
discussed above, deletes the EFI boot path which was used to boot. As long as
there’s a Rescatux image lying around, though, that’s not a problem.&lt;/p&gt;

&lt;h4 id=&quot;add-the-grub-entry&quot;&gt;Add the GRUB entry&lt;/h4&gt;
&lt;p&gt;The GRUB entry should look something like this (put it at the bottom of
&lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/grub.d/40_custom&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;menuentry &quot;System76 Firmware Update&quot; {
        insmod chain
        insmod search_fs_uuid
        search --fs-uuid --no-floppy --set=root TODO-ID
        chainloader /EFI/system76-firmware-update/boot.efi
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In order for it to work for your machine, you need to do two things.&lt;/p&gt;

&lt;h4 id=&quot;find-the-filesystem-uuid&quot;&gt;Find the filesystem UUID&lt;/h4&gt;
&lt;p&gt;Run &lt;code class=&quot;highlighter-rouge&quot;&gt;cfdisk &amp;lt;your disk&amp;gt;&lt;/code&gt; and select your EFI partition to see its filesystem id.
For example, mine is &lt;code class=&quot;highlighter-rouge&quot;&gt;129D-B845&lt;/code&gt;. Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;TODO-ID&lt;/code&gt; in the above GRUB entry with
your id. Here’s what it should look like:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cfdisk /dev/nvme0n1
                                   Disk: /dev/nvme0n1
                 Size: 232.9 GiB, 250059350016 bytes, 488397168 sectors
              Label: gpt, identifier: B7545E19-CF68-4779-BD95-F12C7DC8DDA6

    Device                 Start          End      Sectors      Size Type
&amp;gt;&amp;gt;  /dev/nvme0n1p1          2048      1050623      1048576      512M EFI System         
    /dev/nvme0n1p2       1050624    488397134    487346511    232.4G Linux filesystem
 ┌────────────────────────────────────────────────────────────────────────────────────┐
 │  Partition name: primary                                                           │
 │  Partition UUID: 7761E4D0-5726-45BE-9761-25FC114DE691                              │
 │  Partition type: EFI System (C12A7328-F81F-11D2-BA4B-00A0C93EC93B)                 │
 │ Filesystem UUID: 129D-B845                                                         │
 │Filesystem LABEL: EFI                                                               │
 └────────────────────────────────────────────────────────────────────────────────────┘
   [ Delete ]  [ Resize ]  [  Quit  ]  [  Type  ]  [  Help  ]  [  Write ]  [  Dump  ]

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;move-the-updater-to-the-efi-partition&quot;&gt;Move the updater to the EFI partition&lt;/h4&gt;
&lt;p&gt;As mentioned above, &lt;code class=&quot;highlighter-rouge&quot;&gt;system76-firmware&lt;/code&gt; installs the updater to
&lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi/system76-firmware-update&lt;/code&gt;. If that’s your EFI root, great. Just
update the GRUB entry to not use &lt;code class=&quot;highlighter-rouge&quot;&gt;/EFI/&lt;/code&gt;. If your system is like mine, though,
you’ll need to move the updater to &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi/EFI/system76-firmware-update&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;generate-grubs-new-config&quot;&gt;Generate GRUB’s new config&lt;/h4&gt;
&lt;p&gt;With the new GRUB entry, the final step to get ready for the reboot is to
generate the new GRUB configs.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;grub-mkconfig &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;time-to-update-the-firmware&quot;&gt;Time to update the firmware&lt;/h3&gt;
&lt;p&gt;At this point, a reboot should bring you into GRUB with your new entry. After
selecting your new entry, you should see the updater and you can press enter to
continue. Remember to have the &lt;a href=&quot;http://support.system76.com/articles/laptop-firmware/&quot;&gt;System76
docs&lt;/a&gt; on hand, so you
understand how the update flow should go.&lt;/p&gt;

&lt;p&gt;After a little while, the updater will reboot. Your fans will be on full blast.
&lt;strong&gt;At the GRUB menu, choose the updater again.&lt;/strong&gt; Once you’re back in the updater,
it’ll work for a while more, run some checks, and finally power off. Follow the
System76 docs to jump into BIOS, adopt the defaults, and everything else. When
you’re ready to get back into GNU/Linux, you’ll find your EFI boot path is gone.&lt;/p&gt;

&lt;h3 id=&quot;rescatux&quot;&gt;Rescatux&lt;/h3&gt;
&lt;p&gt;Plug in your Rescatux media, reboot your machine, optionally hit F2 to change
your boot order, if necessary, to make it into the Rescatux menu. The default
option, to find all bootable OSs, will do the trick, so just hit enter and then
choose the first Linux boot option to get back into your typical environment.&lt;/p&gt;

&lt;h3 id=&quot;reinstall-grub&quot;&gt;Reinstall GRUB&lt;/h3&gt;
&lt;p&gt;Since your EFI boot path was wiped by the updater and you probably don’t want to
boot via Rescatux forever, once you’re back in a sane GNU/Linux environment,
just mount your EFI partition and reinstall GRUB. For me, that process looked
like this.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;cfdisk /dev/nvme0n1
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;mount /dev/nvme0n1p1 /boot/efi
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;grub-install &lt;span class=&quot;nt&quot;&gt;--target&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;x86_64-efi &lt;span class=&quot;nt&quot;&gt;--efi-directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/boot/efi &lt;span class=&quot;nt&quot;&gt;--bootloader-id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;grub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Intel Management Engine has now been disabled and its code has been removed.
You can try running the firmware updater again, just to be sure that it detects
everything is up to date.&lt;/p&gt;

&lt;h3 id=&quot;cleanup&quot;&gt;Cleanup&lt;/h3&gt;
&lt;p&gt;Finally, feel free to remove &lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi/EFI/system76-firmware-update&lt;/code&gt; and
&lt;code class=&quot;highlighter-rouge&quot;&gt;/boot/efi/system76-firmware-update&lt;/code&gt;, if they’re still around. You can also
remove the GRUB boot entry from &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/grub.d/40_custom&lt;/code&gt; and re-generate your
GRUB config.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;grub-mkconfig &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;post-update-issues&quot;&gt;Post-update issues&lt;/h3&gt;
&lt;p&gt;In &lt;code class=&quot;highlighter-rouge&quot;&gt;#system76&lt;/code&gt; on Freenode, which is barren and likely not worth joining, I’ve
seen some questions about people losing some fn key functionality after the
update. For me, HDMI didn’t work at all after the update. I unplugged and
tested the HDMI cable and monitor on another machine and concluded that the
firmware update must’ve borked something. &lt;code class=&quot;highlighter-rouge&quot;&gt;xrandr&lt;/code&gt; didn’t show any connections,
but I didn’t look further into it than that, since it was late and I was pleased
enough to’ve finished the update.&lt;/p&gt;

&lt;p&gt;In the morning, HDMI worked as expected, without any hiccups. Maybe the machine
also just need a good night’s rest.&lt;/p&gt;</content><author><name>jeaye</name></author><category term="system76" /><category term="firmware" /><category term="ime" /><category term="arch" /><summary type="html">In the wake of the Intel Management Engine security revelations, System76 developed a firmware upgrade for all of its machines and released an update plan in November 2017. In February 2018, owners of the Oryx Pro were informed that the firmware update was available through System76’s open source firmware updater. For anyone not on System76’s Pop!_OS or similar Debian-based distros, this firmware updater probably didn’t do anything. After waiting patiently for a couple of months for more updates and not seeing any fixes, I dug into how I could get things going. Herein lies the easiest way I found.</summary></entry><entry><title type="html">Switching from solarized to gruvbox</title><link href="https://blog.jeaye.com//2018/02/01/gruvbox/" rel="alternate" type="text/html" title="Switching from solarized to gruvbox" /><published>2018-02-01T00:00:00-08:00</published><updated>2018-02-01T00:00:00-08:00</updated><id>https://blog.jeaye.com//2018/02/01/gruvbox</id><content type="html" xml:base="https://blog.jeaye.com//2018/02/01/gruvbox/">&lt;p&gt;Ever since 2012, I’ve been using
&lt;a href=&quot;https://github.com/altercation/solarized&quot;&gt;solarized dark&lt;/a&gt; for everything I can
possibly configure. From my web browser to my terminal, and everything in
between, I was a solarized user. Alas, I was experiencing regular eye strain,
especially at night, even using solarized dark with low monitor brightness. I
use a large font size (large enough for people to comment on it regularly) and,
as of my recent tests, I have at least 20/20 sight and no known ocular issues.
Still, I was determined I could ease the strain on my eyes.&lt;/p&gt;

&lt;h3 id=&quot;alternatives-to-solarized&quot;&gt;Alternatives to solarized&lt;/h3&gt;
&lt;p&gt;There are too many to list, but one which caught my eye was
&lt;a href=&quot;https://github.com/morhetz/gruvbox&quot;&gt;gruvbox&lt;/a&gt;. Specifically, gruvbox has a
pretty warm palette and minimal emphasis on blue, which is quite a change from
my old setup. Here are some comparison images.&lt;/p&gt;

&lt;div style=&quot;display:flex&quot;&gt;
  &lt;figure style=&quot;flex:50%;margin:2px;&quot;&gt;
    &lt;a href=&quot;/assets/gruvbox/gruvbox-4b9eb65993efe1695320a3c5beee7f9aeff6df68e1ff55c81fc3fa1988021465.png&quot; target=&quot;_blank&quot;&gt;
      &lt;img style=&quot;width:100%&quot; src=&quot;/assets/gruvbox/gruvbox-4b9eb65993efe1695320a3c5beee7f9aeff6df68e1ff55c81fc3fa1988021465.png&quot; integrity=&quot;sha256-S562WZPv4WlTIKPFvu5/mu/232jh/1XIH8P6GYgCFGU=&quot; crossorigin=&quot;anonymous&quot; /&gt;
    &lt;/a&gt;
  &lt;/figure&gt;
  &lt;figure style=&quot;flex:50%;margin:2px;&quot;&gt;
    &lt;a href=&quot;/assets/gruvbox/solarized-dde34f70207073d9c2c8edfdf388053449832066123e926ec502c74e197b45b5.png&quot; target=&quot;_blank&quot;&gt;
      &lt;img style=&quot;width:100%&quot; src=&quot;/assets/gruvbox/solarized-dde34f70207073d9c2c8edfdf388053449832066123e926ec502c74e197b45b5.png&quot; integrity=&quot;sha256-3eNPcCBwc9nCyO3984gFNEmDIGYSPpJuxQLHThl7RbU=&quot; crossorigin=&quot;anonymous&quot; /&gt;
    &lt;/a&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h3 id=&quot;adopting-gruvbox-everywhere&quot;&gt;Adopting gruvbox everywhere&lt;/h3&gt;
&lt;p&gt;gruvbox itself was originally made as just a Vim colorscheme. In its &lt;a href=&quot;https://github.com/morhetz/gruvbox&quot;&gt;official
git repository&lt;/a&gt;, you won’t find support for any
other programs. However, there is a
&lt;a href=&quot;https://github.com/morhetz/gruvbox-contrib&quot;&gt;gruvbox-contrib&lt;/a&gt; repository which
has support for all sorts of themes for editors, shells, and GUI programs. I’m
using the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/morhetz/gruvbox&quot;&gt;Vim colorscheme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Vim &lt;a href=&quot;https://github.com/vim-airline/vim-airline&quot;&gt;airline&lt;/a&gt; theme&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://addons.mozilla.org/en-US/thunderbird/addon/gruvbox-dark-medium/&quot;&gt;Firefox tab theme&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://userstyles.org/styles/137214/gruvbox-dark-everywhere-global-dark-style&quot;&gt;Firefox userstyle&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/i3wm/comments/78dtn7/how_to_change_dmenus_default_colors/&quot;&gt;dmenu colors&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/PandorasFox/i3lock-color&quot;&gt;i3lock colors&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://archive.fo/QSYHd&quot;&gt;tty colors&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;manual i3wm colors&lt;/li&gt;
  &lt;li&gt;manual &lt;a href=&quot;https://aur.archlinux.org/packages/hsetroot&quot;&gt;hsetroot&lt;/a&gt; color&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;further-blue-light-reduction&quot;&gt;Further blue light reduction&lt;/h3&gt;
&lt;p&gt;To take things a step further, I also installed
&lt;a href=&quot;http://jonls.dk/redshift/&quot;&gt;Redshift&lt;/a&gt;, which globally affects the warmth of the
screen’s colors based on the time of day. That is, as the sun goes down,
Redshift will gradually make the screen have warmer colors, with the least
amount of blue at night. Redshift is conveniently in the official Arch
repositories and it works out of the box. I just added the following to my
&lt;code class=&quot;highlighter-rouge&quot;&gt;~/.xinitrc&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Remove blue light at night&lt;/span&gt;
redshift-gtk &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;verdict&quot;&gt;Verdict&lt;/h3&gt;
&lt;p&gt;Has gruvbox with Redshift actually helped? When I’m using the computer,
especially at night, my eyes do still feel strained. However, if I disable
Redshift and switch solarized dark back on, it’s very tough to look at the
screen again! There’s such a stark difference between looking at gruvbox +
Redshift and looking at solarized dark. With that said, I consider this decision
a definite win. I’d also note that gruvbox hasn’t seemed any more difficult to
view than solarized during the day as well.&lt;/p&gt;

&lt;p&gt;Surprisingly, Redshift works well even while playing games on Steam. While
playing Rocket League at night, for example, Redshift warms up so much of the
blue that it’s shocking when I temporarily disable it to compare.&lt;/p&gt;

&lt;p&gt;As for the remaining eye strain, perhaps it’s best to just finish computing
earlier at night and schedule more time for reading.&lt;/p&gt;</content><author><name>jeaye</name></author><category term="solarized" /><category term="gruvbox" /><category term="blue light" /><category term="fatigue" /><category term="sleep" /><summary type="html">Ever since 2012, I’ve been using solarized dark for everything I can possibly configure. From my web browser to my terminal, and everything in between, I was a solarized user. Alas, I was experiencing regular eye strain, especially at night, even using solarized dark with low monitor brightness. I use a large font size (large enough for people to comment on it regularly) and, as of my recent tests, I have at least 20/20 sight and no known ocular issues. Still, I was determined I could ease the strain on my eyes.</summary></entry><entry><title type="html">Don’t abandon Mozilla Firefox just yet</title><link href="https://blog.jeaye.com//2017/12/16/firefox/" rel="alternate" type="text/html" title="Don't abandon Mozilla Firefox just yet" /><published>2017-12-16T00:00:00-08:00</published><updated>2017-12-16T00:00:00-08:00</updated><id>https://blog.jeaye.com//2017/12/16/firefox</id><content type="html" xml:base="https://blog.jeaye.com//2017/12/16/firefox/">&lt;p&gt;Just today, a &lt;a href=&quot;https://drewdevault.com/2017/12/16/Firefox-is-on-a-slippery-slope.html&quot;&gt;diatribe against
Mozilla&lt;/a&gt;
made it to the front page of Hacker News, gathering a &lt;a href=&quot;https://news.ycombinator.com/item?id=15940144&quot;&gt;great deal of
discussion&lt;/a&gt;. Unfortunately, a
significant portion of that discussion was around choosing alternatives to
Firefox, forking it, or otherwise abandoning ship. Please, if you do care about
your privacy, security, and voice in the matter, refrain from the brash
decisions and read on.&lt;/p&gt;

&lt;h3 id=&quot;some-of-the-key-issues&quot;&gt;Some of the key issues&lt;/h3&gt;
&lt;p&gt;Briefly, some of the issues being discussed are the uninvited introduction of
&lt;a href=&quot;https://getpocket.com/firefox/&quot;&gt;Firefox Pocket&lt;/a&gt;, as well as the &lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=1424977&quot;&gt;side-loading
of plugins&lt;/a&gt; by Mozilla,
used in aid of promotion of a TV show. The effect of this promotion is mostly
benign, but the problem is that this was done entirely unbeknownst to the user
and was enabled by default.&lt;/p&gt;

&lt;h3 id=&quot;so-time-for-a-new-browser&quot;&gt;So, time for a new browser&lt;/h3&gt;
&lt;p&gt;This is the next logical step for a number of participants thus far, but I think
they’re not quite considering the implications of using those browsers, as well
as the possibility that Mozilla is not a completely lost cause. First, a bit on
forks and soft forks, as well as Chrome and Chromium.&lt;/p&gt;

&lt;h3 id=&quot;chrome-and-chromium&quot;&gt;Chrome and Chromium&lt;/h3&gt;
&lt;p&gt;Perhaps the worst result of this assault is that Firefox users will jump over to
Chrome. Put simply, since the issue here is about privacy and control over one’s
browser, that makes absolutely no sense. This is a serious problem with
diatribes against Mozilla for something so relatively small; compared to Google,
and the lack of privacy you have within its ecosystem, these Mozilla nitpicks
are bantam.&lt;/p&gt;

&lt;p&gt;Even with Chromium, there are still in-built Google services which impose upon
your privacy. This has lead to the soft fork
&lt;a href=&quot;https://github.com/Eloston/ungoogled-chromium&quot;&gt;ungoogled-chromium&lt;/a&gt;, but forks
have their own baggage to carry as well.&lt;/p&gt;

&lt;h3 id=&quot;forks-of-firefox-or-chromium&quot;&gt;Forks of Firefox or Chromium&lt;/h3&gt;
&lt;p&gt;To start with, note that browsers are one of the most complex programs running
on your OS. Interfacing with the internet, various sites, technologies, etc.
means that browsers need to be constantly updating to remain secure. Running any
version of Firefox, for example, which is not either the latest or the latest
&lt;a href=&quot;https://www.mozilla.org/en-US/firefox/organizations/&quot;&gt;ESR&lt;/a&gt;, is putting yourself
at risk of all of the known exploits which have been found since.&lt;/p&gt;

&lt;p&gt;Hard forks are at serious risk of growing stale behind the upstream parent.
Furthermore, any hard fork with a focus on privacy and security will need its
own team of developers specifically looking for and quickly patching
vulnerabilities in both the new code, as well as the originally forked code.
Mozilla pays its security engineers &lt;a href=&quot;https://www.glassdoor.com/Salary/Mozilla-Security-Engineer-Salaries-E19129_D_KO8,25.htm&quot;&gt;over $150K per
year&lt;/a&gt;
to focus on this sort of work full-time. Replicating that level of commitment
with a hard fork is incredibly difficult.&lt;/p&gt;

&lt;h3 id=&quot;soft-forks&quot;&gt;Soft forks&lt;/h3&gt;
&lt;p&gt;A generally safer bet is a soft fork, which follows the upstream parent closely,
but typically just removes some features, or makes some relatively small changes
which allow it to remain quite compatible with its parent. Examples of these
would be &lt;a href=&quot;https://www.waterfoxproject.org/&quot;&gt;Waterfox&lt;/a&gt; and the previously linked
&lt;a href=&quot;https://github.com/Eloston/ungoogled-chromium&quot;&gt;ungoogled-chromium&lt;/a&gt;. While it’s
easier for these projects to follow the progress of the parent, it’s still not
guaranteed. Furthermore, the removal of some feature in the soft fork, or the
preservation of an old feature which was removed in the parent, may introduce
new vulnerabilities which are unique to the soft fork.&lt;/p&gt;

&lt;p&gt;Catching these new issues would require security-minded individuals working on
the project regularly. Ensuring that the soft fork is following the parent
closely remains up to the whim of those running the project. In short, it’s not
nearly as reliable as sticking with a company such as Mozilla, who has been
invested in developing Firefox for over 15 years.&lt;/p&gt;

&lt;h3 id=&quot;what-to-do-with-mozilla&quot;&gt;What to do with Mozilla&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;So, don’t bite the hand that feeds?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No, that’s not my point. Mozilla shouldn’t have side-loaded plugins in my
browser to promote a show of which I’d never even heard. I also still don’t like
seeing Pocket there, an uninvited guest in my otherwise tidy browsing
environment. Still, that doesn’t mean that Mozilla is evil and I should try my
luck with Google or some fork on Github which managed to get a few hundred
stars. Instead, it’s important to voice the opinion to Mozilla that this wasn’t
cool and it should be removed. Jumping to a one-off fork is not a sustainable
option; working with Mozilla, as a community, to show how pissed or pleased we
are, at any given moment, is a much more sustainable option.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.mozilla.org/en-US/about/manifesto/details/&quot;&gt;Mozilla
manifesto&lt;/a&gt; contains a
few great points relevant to this topic. One of which is the involvement of
commercial products in the internet.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;Commercial involvement in the development of the Internet brings many benefits; a balance between commercial profit and public benefit is critical.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;This balance is hard to strike. It’s the sort of struggle which requires the
community to be as active with Mozilla as the companies for which Mozilla is
advertising. That’s not to say it’s the community’s fault for this happening;
it’s to say that Mozilla isn’t perfect and we occasionally need to slap it back
into shape. Right now, it’d be great to see more people herding Mozilla and
fewer assuming all is lost.&lt;/p&gt;</content><author><name>jeaye</name></author><category term="firefox" /><category term="pocket" /><category term="mozilla" /><category term="chromium" /><category term="google" /><category term="privacy" /><category term="security" /><summary type="html">Just today, a diatribe against Mozilla made it to the front page of Hacker News, gathering a great deal of discussion. Unfortunately, a significant portion of that discussion was around choosing alternatives to Firefox, forking it, or otherwise abandoning ship. Please, if you do care about your privacy, security, and voice in the matter, refrain from the brash decisions and read on.</summary></entry><entry><title type="html">A guide to using NoScript 10.x</title><link href="https://blog.jeaye.com//2017/11/30/noscript/" rel="alternate" type="text/html" title="A guide to using NoScript 10.x" /><published>2017-11-30T00:00:00-08:00</published><updated>2017-11-30T00:00:00-08:00</updated><id>https://blog.jeaye.com//2017/11/30/noscript</id><content type="html" xml:base="https://blog.jeaye.com//2017/11/30/noscript/">&lt;p&gt;When Firefox 57 arrived, NoScript 5 users were left with the unsettling reality
of not being able to selectively filter JavaScript until the next version was
released, a week or so later. Worse, its release was poorly received, due to
bugs and a brand new UI. Now that the dust has settled, and the bugs have been
vanquished, NoScript users are still presented with the foreign UI and little to
no official documentation. This post will serve as a guide for both old and new
NoScript 10 users to get up to speed.&lt;/p&gt;

&lt;h3 id=&quot;the-trust-levels&quot;&gt;The trust levels&lt;/h3&gt;
&lt;p&gt;Each domain in NoScript 10 has a trust level. By default, each domain is under
the &lt;strong&gt;Default&lt;/strong&gt; trust level, but you may find your old NoScript domains already
under the &lt;strong&gt;Trusted&lt;/strong&gt; level. These are familiar to previous NoScript users: the
default is not to allow JavaScript, but domains can be explicitly &lt;strong&gt;Trusted&lt;/strong&gt; or
&lt;strong&gt;Temporarily Trusted&lt;/strong&gt;. NoScript 10 also adds two more levels: &lt;strong&gt;Untrusted&lt;/strong&gt;
and &lt;strong&gt;Custom&lt;/strong&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/intro-ab3b72460d678a8c7bb2e733eac2ae91eb758029e1598710422ba757335a27c5.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/intro-ab3b72460d678a8c7bb2e733eac2ae91eb758029e1598710422ba757335a27c5.png&quot; integrity=&quot;sha256-qztyRg1niox7sucz6sKuket1gCnhWYcQQiunVzNaJ8U=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;p&gt;Though the UI makes this a bit unclear, these trust levels are columns. For each
domain, one button is selected, meaning one trust level.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/trust-levels-716385ae81a5ffdd5850518003fa6796203171e0af5b4025dc65ca8da5651ad1.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/trust-levels-716385ae81a5ffdd5850518003fa6796203171e0af5b4025dc65ca8da5651ad1.png&quot; integrity=&quot;sha256-cWOFroGl/91YUFGAA/pnliAxceCvW0Al3GXKjaVlGtE=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;editing-trust-levels&quot;&gt;Editing trust levels&lt;/h3&gt;
&lt;p&gt;Each trust level can be edited to describe what happens when you set a domain to
that level. For &lt;strong&gt;Default&lt;/strong&gt;, &lt;strong&gt;Temporarily Trusted&lt;/strong&gt;, &lt;strong&gt;Trusted&lt;/strong&gt;, and
&lt;strong&gt;Untrusted&lt;/strong&gt;, that configuration is global. That is, you you say that the
&lt;strong&gt;Default&lt;/strong&gt; trust level should trust fonts (by checking the &lt;em&gt;fonts&lt;/em&gt; checkbox),
but nothing else, then &lt;em&gt;every domain&lt;/em&gt; in that trust level will have that
setting. When you modify the &lt;strong&gt;Custom&lt;/strong&gt; trust level, it’s per-domain.&lt;/p&gt;

&lt;p&gt;To edit a global trust level, enter the NoScript settings page by clicking the
settings button in the NoScript menu.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/settings-button-b73c4db4d0ba15b48ea6fc2c409eb6759e315a045c76f63637d2f069a576d864.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/settings-button-b73c4db4d0ba15b48ea6fc2c409eb6759e315a045c76f63637d2f069a576d864.png&quot; integrity=&quot;sha256-tzxNtNC6FbSOpvwsQJ62dZ4xWgRcdvY2N9LwaaV22GQ=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;p&gt;Once you’re there, you can specify the global properties for each trust level.
Note that the properties for &lt;strong&gt;Temporarily Trusted&lt;/strong&gt; reflect whatever you have
set for &lt;strong&gt;Trusted&lt;/strong&gt;. The only difference is that &lt;strong&gt;Temporarily Trusted&lt;/strong&gt; domains
are only trusted for the current session.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/edit-trust-levels-59ee2d87d8d8faf33bc1f117245489f5b87e571de928da165f8e44add1940a8d.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/edit-trust-levels-59ee2d87d8d8faf33bc1f117245489f5b87e571de928da165f8e44add1940a8d.png&quot; integrity=&quot;sha256-We4th9jY+vM7wfEXJFSJ9bh+Vx3pKNoWX45ErdGUCo0=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;safe-defaults&quot;&gt;Safe defaults&lt;/h3&gt;
&lt;p&gt;NoScript 10 allows for more control over what’s filtered. Scripts, objects,
media, frames, fonts, WebGL, fetch, and other things can all be filtered. Since
you’re using NoScript for privacy, I recommend changing your &lt;strong&gt;Default&lt;/strong&gt; trust
level to uncheck all items. If you trust a domain completely, add it to the
&lt;strong&gt;Trusted&lt;/strong&gt; level. If you only want some items from it, like fonts and WebGL,
then use a &lt;strong&gt;Custom&lt;/strong&gt; trust level for that domain.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/safe-defaults-11193465c21045bce60df3762bd7b9b0ac95831fe379fb66bc2ac9ca47a79522.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/safe-defaults-11193465c21045bce60df3762bd7b9b0ac95831fe379fb66bc2ac9ca47a79522.png&quot; integrity=&quot;sha256-ERk0ZcIQRbzmDfN2K9e5sKyVgx/jeftmvCrJykenlSI=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;top-level-domain-configuration&quot;&gt;Top-level domain configuration&lt;/h3&gt;
&lt;p&gt;When browsing sites and using NoScript 10, you’ll likely notice that a domain
will show up multiple times within the NoScript menu. This is because NoScript
10 allows you to modify the trust level of the specific domain used (such as
&lt;code class=&quot;highlighter-rouge&quot;&gt;blog.jeaye.com&lt;/code&gt;), as well as the entire top-level domain (such as &lt;code class=&quot;highlighter-rouge&quot;&gt;…jeaye.com&lt;/code&gt;).
If you set a top-level domain to a specific trust level, that trust level will
apply to all sub-domains as well. This is very handy for marking entire
ad/tracking domains as &lt;strong&gt;Untrusted&lt;/strong&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/sub-domains-3a4ba90ded45a00ee26970a9f775b6d77c2f4ec277f3073c2cacf6cbf9541d1d.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/sub-domains-3a4ba90ded45a00ee26970a9f775b6d77c2f4ec277f3073c2cacf6cbf9541d1d.png&quot; integrity=&quot;sha256-OkupDe1FoA7iaXCp93W213wvTsJ38wc8LKz2y/lUHR0=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;permanent-trust-levels&quot;&gt;Permanent trust levels&lt;/h3&gt;
&lt;p&gt;When getting a site to work, I recommend only marking the domains as
&lt;strong&gt;Temporarily Trusted&lt;/strong&gt;. Once the site is in working condition, you can upgrade
them to permanently &lt;strong&gt;Trusted&lt;/strong&gt;. If you don’t plan on returning to the site
often, consider leaving the trust level temporary.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/temporary-trust-ed0597b808585507695353897c4b093238499513e36830d06c310ead0ef6cbda.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/temporary-trust-ed0597b808585507695353897c4b093238499513e36830d06c310ead0ef6cbda.png&quot; integrity=&quot;sha256-7QWXuAhYVQdpU1OJfEsJMjhJlRPjaDDQbDEOrQ72y9o=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;trusting-https-only&quot;&gt;Trusting HTTPS only&lt;/h3&gt;
&lt;p&gt;NoScript 10 provides the ability to trust a domain only if it’s through a secure
connection. You can tell if this is enabled by whether or not the lock, which
appears on that domain’s row, is green or red. If it’s red, then the domain will
be trusted even through unencrypted connections. This is a concern, since
unencrypted connections are vulnerable to MITM attacks and you may be trusting
arbitrary and nefarious JavaScript. I recommend making sure the lock is always
green (you can click on the lock to toggle it).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If the site only works through HTTP, then trusting HTTPS only will
effectively change the site’s trust level back to &lt;strong&gt;Default&lt;/strong&gt;. You may need to
keep the lock red for trusted sites which only use HTTP, but you should think
twice about trusting anything over HTTP.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/green-lock-6f1c65eab204268a287de05e9c2a193587a524370d347c6d1307b62742944021.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/green-lock-6f1c65eab204268a287de05e9c2a193587a524370d347c6d1307b62742944021.png&quot; integrity=&quot;sha256-bxxl6rIEJooofeBenCoZNYelJDcNNHxtEwe2J0KUQCE=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
  &lt;a href=&quot;/assets/noscript/red-lock-6f8b16d8cdf11a4b3a37f9a75b37a84aa76bb8cce174a7ef7b7dfad681fd68b8.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/red-lock-6f8b16d8cdf11a4b3a37f9a75b37a84aa76bb8cce174a7ef7b7dfad681fd68b8.png&quot; integrity=&quot;sha256-b4sW2M3xGks6N/mnWzeoSqdruMzhdKfve3361oH9aLg=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;p&gt;For previous NoScript users, I also recommend going back through your whole
trusted list and setting all domains to have a green lock. You can get to that
list by clicking the options button within the NoScript menu. There isn’t an
automatic process for marking them all green yet, but it only took me 10 minutes
or so to both ensure all locks are green and do some cleaning of old domains I
no longer need to trust.&lt;/p&gt;

&lt;figure&gt;
  &lt;a href=&quot;/assets/noscript/all-green-f0ec02714bfbbc22aec5f5f9e49e387d6f703a7831f1e594dbddbd30c1d06d16.png&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;/assets/noscript/all-green-f0ec02714bfbbc22aec5f5f9e49e387d6f703a7831f1e594dbddbd30c1d06d16.png&quot; integrity=&quot;sha256-8OwCcUv7vCKuxfX55J44fW9wOngx8eWU2929MMHQbRY=&quot; crossorigin=&quot;anonymous&quot; /&gt;
  &lt;/a&gt;
  &lt;br /&gt; &lt;br /&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;the-remaining-ui&quot;&gt;The remaining UI&lt;/h3&gt;
&lt;p&gt;With all of that covered, the only two remaining buttons are for revoking all
temporary permissions and temporarily trusting the whole page. You can mouse
over each of them to see a tool tip for what they do; it’s analogous to the
previous NoScript’s behavior. Finally, at this point, you know all you need to
effectively use NoScript 10. Don’t leave home without it!&lt;/p&gt;</content><author><name>jeaye</name></author><category term="firefox" /><category term="noscript" /><category term="tutorial" /><category term="privacy" /><category term="security" /><category term="tutorial" /><summary type="html">When Firefox 57 arrived, NoScript 5 users were left with the unsettling reality of not being able to selectively filter JavaScript until the next version was released, a week or so later. Worse, its release was poorly received, due to bugs and a brand new UI. Now that the dust has settled, and the bugs have been vanquished, NoScript users are still presented with the foreign UI and little to no official documentation. This post will serve as a guide for both old and new NoScript 10 users to get up to speed.</summary></entry><entry><title type="html">The five common forms of Clojure keywords</title><link href="https://blog.jeaye.com//2017/10/31/clojure-keywords/" rel="alternate" type="text/html" title="The five common forms of Clojure keywords" /><published>2017-10-31T00:00:00-07:00</published><updated>2017-10-31T00:00:00-07:00</updated><id>https://blog.jeaye.com//2017/10/31/clojure-keywords</id><content type="html" xml:base="https://blog.jeaye.com//2017/10/31/clojure-keywords/">&lt;p&gt;Depending on which libraries are being used, Clojure has a handful of various
idiomatic forms keywords can take. When approaching some forms, like
those in &lt;a href=&quot;http://www.datomic.com/&quot;&gt;Datomic&lt;/a&gt;, the overall intention may not be
immediately clear. For a new Clojure developer, it may also be unclear which
form should be the default, and why. This post aims to add some clarity to the
subject and it applies to both Clojure and ClojureScript. Along with
explanations of each keyword form is a recommendation for when to use it and
when to opt for something else.&lt;/p&gt;

&lt;h3 id=&quot;brief-the-five-common-forms&quot;&gt;Brief: the five common forms&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:foo&lt;/code&gt;, which is just your plain old keyword&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;::foo&lt;/code&gt;, which is a namespaced keyword for the current namespace&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:my.ns/name&lt;/code&gt;, which is a namespaced keyword for a valid namespace&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;::my/name&lt;/code&gt;, which uses the &lt;code class=&quot;highlighter-rouge&quot;&gt;:as&lt;/code&gt; alias to achieve the same as form #3&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;:something/foo&lt;/code&gt;, which is commonly used with Datomic and doesn’t actually map to a valid namespace&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;plain-old-keywords&quot;&gt;Plain old keywords&lt;/h3&gt;
&lt;p&gt;These will show up most often in the Clojure and ClojureScript available on the
web.  They’re easy to type, they don’t require any dependencies, and they make
it easy for anyone to consume. That convenience, however, comes at a cost; they
can easily cause name collisions, they don’t convey ownership, and, for those
reasons, they can’t be used to name specs with &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.spec&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;example&quot;&gt;Example&lt;/h4&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;recommendation&quot;&gt;Recommendation&lt;/h4&gt;
&lt;p&gt;Avoid plain old keywords by default. If you have a map of
data being passed around, for example, namespace the keywords (and consider
adding specs for the data). If you have an “enum,” meaning one in a discrete set
of possible keywords, also namespace them. The only time when a plain old
keyword’s convenience overcomes its cost is within a simple API or DSL with no
middleware, so no possibility for collisions. Examples of this would be:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;; Simple keyword arguments.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;json/read-str&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:key-fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;keyword&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:req&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;namespaced-keywords&quot;&gt;Namespaced keywords&lt;/h3&gt;
&lt;p&gt;This applies to forms #2, #3, and #4 specifically. These are necessary for
specs. They convey ownership, since they’re tied to a valid namespace, they
completely avoid the issue of name collision, and they can help explicitly spell
out dependencies. Though they may feel like extra work, since you will need to
treat them as dependencies, willy-nilly access to data is not a good thing and
being explicit about ownership is.&lt;/p&gt;

&lt;h4 id=&quot;example-1&quot;&gt;Example&lt;/h4&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;; Form 2.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my.vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::2d&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:req&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Form 4.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my.game-object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my.vector&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::position&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::v/2d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::v/x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;::v/y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Form 3. Some declaractive config or scene.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:my.game/objects&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:my.game-object/id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:coltrane&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:my.game-object/position&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:my.vector/x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                              &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:my.vector/y&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;recommendation-1&quot;&gt;Recommendation&lt;/h4&gt;
&lt;p&gt;Forms #2 and #4 should be your default in Clojure and ClojureScript. Within the
same namespace, &lt;code class=&quot;highlighter-rouge&quot;&gt;::foo&lt;/code&gt; is only one more character than &lt;code class=&quot;highlighter-rouge&quot;&gt;:foo&lt;/code&gt;, but it carries
significantly more data. When you want to access some other system’s data from
your app state, for example, you have a dependency on that data. Tying that
dependency on a namespace level, through a &lt;code class=&quot;highlighter-rouge&quot;&gt;(:require [my.ns :as my-ns])&lt;/code&gt; allows
you to then use the shorthand #4 form &lt;code class=&quot;highlighter-rouge&quot;&gt;::my-ns/foo&lt;/code&gt;. If you detect cyclical
dependencies and can’t reorganize, or you need to avoid the require for another
reason, then the #3 form can be used. Similarly, within your &lt;code class=&quot;highlighter-rouge&quot;&gt;config.edn&lt;/code&gt;, or
similar, you’ll use form #3, since you likely have no requires.&lt;/p&gt;

&lt;h3 id=&quot;grouped-keywords&quot;&gt;Grouped keywords&lt;/h3&gt;
&lt;p&gt;Grouped keywords (my own terminology) match form #5. Syntactically, grouped
keywords are namespaced keywords, but they’re not tied to a valid namespace.
Instead, the namespace segment is used for some logical grouping. Datomic uses
this for grouping attributes, like &lt;code class=&quot;highlighter-rouge&quot;&gt;:db/id&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;:user/name&lt;/code&gt;. Note that grouped
keywords may also use nested group names, like &lt;code class=&quot;highlighter-rouge&quot;&gt;:db.type/long&lt;/code&gt;. These don’t map
to valid namespaces.&lt;/p&gt;

&lt;h4 id=&quot;example-2&quot;&gt;Example&lt;/h4&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order-schema&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/ident&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:order/items&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/valueType&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db.type/ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/cardinality&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db.cardinality/many&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/isComponent&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/ident&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:item/id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/valueType&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db.type/ref&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/cardinality&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db.cardinality/one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/ident&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:item/count&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/valueType&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db.type/long&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db/cardinality&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:db.cardinality/one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/transact&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:tx-data&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order-schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;recommendation-2&quot;&gt;Recommendation&lt;/h4&gt;
&lt;p&gt;Avoid these in most situations, but use them where
idiomatic. Given that you may want specs for these keywords anyway, I would
recommend replacing &lt;code class=&quot;highlighter-rouge&quot;&gt;:db/id&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;::db/id&lt;/code&gt; and building a &lt;code class=&quot;highlighter-rouge&quot;&gt;my-app.db&lt;/code&gt; namespace
with the correct specs; use your own good judgement.&lt;/p&gt;

&lt;h3 id=&quot;dotted-keywords&quot;&gt;Dotted keywords&lt;/h3&gt;
&lt;p&gt;Finally, some Clojure libraries allow, or encourage, the use of dotted keywords
(my own terminology). Dotted keywords are plain old keywords, but they’re
specifically used for string building. They’re somewhat more convenient than
using strings, since keywords just have a prefix and needn’t be enclosed in
quotes. They are less common and aren’t necessarily recommended, but, within a
DSL, they can feel quite natural. Due to their uncommonness, dotted keywords
aren’t included in the five common forms; they’re included here as an honorable
mention.&lt;/p&gt;

&lt;h4 id=&quot;example-honeysql&quot;&gt;Example: HoneySQL&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://clojars.org/honeysql&quot;&gt;&lt;img src=&quot;https://img.shields.io/clojars/v/honeysql.svg&quot; alt=&quot;Clojars Project&quot; /&gt;&lt;/a&gt;
HoneySQL is an excellent query builder for numerous SQL databases.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;; Keywords like :f.a are used for string building.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:select&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:where&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:f.a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql/format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SELECT a, b, c FROM foo WHERE f.a = ?&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Keywords are also used with symbols to convey operations.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:&amp;lt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sql/format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SELECT * FROM foo WHERE (a = ? AND b &amp;lt; ?)&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;example-cljs-oops&quot;&gt;Example: cljs-oops&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://clojars.org/binaryage/oops&quot;&gt;&lt;img src=&quot;https://img.shields.io/clojars/v/binaryage/oops.svg&quot; alt=&quot;Clojars Project&quot; /&gt;&lt;/a&gt;
cljs-oops is an essential library for any ClojureScript being compiled with
&lt;code class=&quot;highlighter-rouge&quot;&gt;:advanced&lt;/code&gt; optimizations.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;; Keywords can be used for accessing members of JS objects.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;oget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-js-obj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:my-member&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Nested members can be accessed using dotted keywords.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;oget&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-js-obj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:transform.position.x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Calling functions is much the same.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js/Math&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:abs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-debt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Keywords in Clojure are certainly versatile. This post has covered plain old
keywords, three forms of namespaced keywords, grouped keywords, and dotted
keywords. You should now also know their common uses and pitfalls. When in
doubt, namespace your keywords to convey explicit ownership of data and prevent
name collisions. When building a DSL without middleware or when using keyword
arguments, plain old keywords will probably do. Where possible, &lt;a href=&quot;/2017/05/31/clojure-spec/&quot;&gt;use
clojure.spec&lt;/a&gt;!&lt;/p&gt;</content><author><name>jeaye</name></author><category term="clojure" /><category term="keyword" /><summary type="html">Depending on which libraries are being used, Clojure has a handful of various idiomatic forms keywords can take. When approaching some forms, like those in Datomic, the overall intention may not be immediately clear. For a new Clojure developer, it may also be unclear which form should be the default, and why. This post aims to add some clarity to the subject and it applies to both Clojure and ClojureScript. Along with explanations of each keyword form is a recommendation for when to use it and when to opt for something else.</summary></entry><entry><title type="html">Async/await in ClojureScript with Promesa</title><link href="https://blog.jeaye.com//2017/09/30/clojurescript-promesa/" rel="alternate" type="text/html" title="Async/await in ClojureScript with Promesa" /><published>2017-09-30T00:00:00-07:00</published><updated>2017-09-30T00:00:00-07:00</updated><id>https://blog.jeaye.com//2017/09/30/clojurescript-promesa</id><content type="html" xml:base="https://blog.jeaye.com//2017/09/30/clojurescript-promesa/">&lt;p&gt;JavaScript (ES7/ES2016) introduced &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; as a clean way of working
with
&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises&quot;&gt;promises&lt;/a&gt;.
When porting this sort of asynchronous code to ClojureScript, it may be
disappointing that there’s no equivalent language feature. Some may say “use
&lt;a href=&quot;https://github.com/clojure/core.async&quot;&gt;core.async&lt;/a&gt;,” which is a fine
suggestion, but it may not work with the existing JS promises/thenables.
This is where &lt;a href=&quot;https://github.com/funcool/promesa&quot;&gt;promesa&lt;/a&gt; comes in. promesa is
a Clojure/Script library for working with native promises; it also provides
macro support for &lt;code class=&quot;highlighter-rouge&quot;&gt;async&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; and uses &lt;code class=&quot;highlighter-rouge&quot;&gt;core.async&lt;/code&gt; machinery behind the
scenes.&lt;/p&gt;

&lt;h3 id=&quot;the-javascript&quot;&gt;The JavaScript&lt;/h3&gt;
&lt;p&gt;This example references &lt;a href=&quot;http://appium.io/&quot;&gt;Appium&lt;/a&gt; and
&lt;a href=&quot;http://webdriver.io/&quot;&gt;webdriver.io&lt;/a&gt; since they’re what I was using when
initially researching promesa. This isn’t the best example usage of &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt;, but
it allows the code to read as though it works synchronously, even though each
line is awaiting a promise.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sign_in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;timeoutsImplicitWait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kr&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;waitForVisible&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;~email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;~email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;test@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;~sign-in&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;kr&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;awaiting-a-js-promise&quot;&gt;Awaiting a JS promise&lt;/h3&gt;
&lt;p&gt;The first attempt won’t be the cleanest, but it’ll mimic the functionality. In
order to turn a JS promise, or, more correctly, any JS thenable, into a
&lt;a href=&quot;http://bluebirdjs.com/docs/getting-started.html&quot;&gt;bluebird&lt;/a&gt; promise (which is
what promesa uses behind the scenes), one needs to call &lt;code class=&quot;highlighter-rouge&quot;&gt;js/Promise.resolve&lt;/code&gt;.
After resolving, the thenable will be a bluebird promise and all of the niceties
of promesa will be available.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-app.test.sign-in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oops.core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:refer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;promesa.core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;promesa.async-cljs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:refer-macros&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sign-in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; async is a promesa macro to allow for awaiting promises.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:timeoutsImplicitWait&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; We resolve the thenable into a bluebird promise...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js/Promise.resolve&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Then we await the promise, just like in the JS version.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p/await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; The repeated resolve and await are an eye sore, but we can't just put them&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; into a fn, since await can only be used within the async form. We need a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; macro.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:waitForVisible&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;~email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js/Promise.resolve&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p/await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:element&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;~email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:setValue&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js/Promise.resolve&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p/await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:click&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;~sign-in&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js/Promise.resolve&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p/await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:pause&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;js/Promise.resolve&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p/await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;removing-some-redundancy&quot;&gt;Removing some redundancy&lt;/h3&gt;
&lt;p&gt;Cleaning up the duplication can be done with a simple &lt;code class=&quot;highlighter-rouge&quot;&gt;await-&amp;gt;&lt;/code&gt; macro.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;; This must be a cljc file.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-app.test.util.macro&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;promesa.core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clj&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmacro&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;await-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thenable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thens&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
     &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thenable&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;~@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;thens&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'js/Promise.resolve&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p/await&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-app.test.sign-in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oops.core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:refer&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;promesa.core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;promesa.async-cljs&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:refer-macros&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-app.test.util.macro&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:refer-macros&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;await-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sign-in&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:timeoutsImplicitWait&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:waitForVisible&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;~email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:element&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;~email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:setValue&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:click&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;~sign-in&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;await-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:pause&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
             &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ocall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;sticking-with-coreasync&quot;&gt;Sticking with core.async&lt;/h3&gt;
&lt;p&gt;For those interested in sticking with core.async, perhaps due to an existing
dependency, there is
&lt;a href=&quot;https://github.com/jamesmacaulay/cljs-promises&quot;&gt;cljs-promises&lt;/a&gt;, which allows
the conversion of JS promises into async channels. Unlike bluebird’s resolve,
cljs-promises doesn’t work with all thenables though, so it’s not as flexible as
promesa in that regard.&lt;/p&gt;</content><author><name>jeaye</name></author><category term="tutorial" /><category term="clojure" /><category term="promesa" /><summary type="html">JavaScript (ES7/ES2016) introduced async and await as a clean way of working with promises. When porting this sort of asynchronous code to ClojureScript, it may be disappointing that there’s no equivalent language feature. Some may say “use core.async,” which is a fine suggestion, but it may not work with the existing JS promises/thenables. This is where promesa comes in. promesa is a Clojure/Script library for working with native promises; it also provides macro support for async/await and uses core.async machinery behind the scenes.</summary></entry><entry><title type="html">The state of code quality tools in Clojure</title><link href="https://blog.jeaye.com//2017/08/31/clojure-code-quality/" rel="alternate" type="text/html" title="The state of code quality tools in Clojure" /><published>2017-08-31T00:00:00-07:00</published><updated>2017-08-31T00:00:00-07:00</updated><id>https://blog.jeaye.com//2017/08/31/clojure-code-quality</id><content type="html" xml:base="https://blog.jeaye.com//2017/08/31/clojure-code-quality/">&lt;p&gt;There are several projects for verifying quality and correctness in Clojure code
bases.  When surveying how they’ll work on a distributed Clojure server built
for Heroku &amp;amp; Postgres, I took some notes on the issues that came up. These notes
represent the current state of things, as well as how we, the Clojure community,
can help move them forward.&lt;/p&gt;

&lt;p&gt;Out of the nine projects evaluated, only three of them worked, and only two were
immediately useful for continuous integration
(&lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_integration&quot;&gt;CI&lt;/a&gt;). It’s worth noting
that the code base being tested is nothing special, in terms of features used,
but it does contain cljc files shared between Clojure and ClojureScript and it
makes heavy use of spec and namespace-qualified keywords. Apparently this causes
a lot of issues with tooling!&lt;/p&gt;

&lt;h3 id=&quot;project-eastwood&quot;&gt;Project: &lt;a href=&quot;https://github.com/jonase/eastwood&quot;&gt;eastwood&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jonase/eastwood&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.2.4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Eastwood is a great looking linter for Clojure. It’s designed to find all sorts
of issues and code smells, but it’s unfortunately unable to run on the server
code, since it doesn’t support namespaced map literals. In the namespaces where
it does run, it raises the same false-positive over and over.&lt;/p&gt;

&lt;h4 id=&quot;relevant-tickets&quot;&gt;Relevant tickets&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jonase/eastwood/issues/201&quot;&gt;Namespaced maps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jonase/eastwood/issues/227&quot;&gt;False positives&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;project-yagni&quot;&gt;Project: &lt;a href=&quot;https://github.com/venantius/yagni&quot;&gt;yagni&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;venantius/yagni&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;yagni is an acronym for &lt;a href=&quot;https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it&quot;&gt;You Aren’t Gonna Need
It&lt;/a&gt; and the project is
a dead code finder (the only one of its kind, in the Clojure world).
Unfortunately, it doesn’t support reader conditionals and it also chokes on the
usage of spec.&lt;/p&gt;

&lt;h4 id=&quot;relevant-tickets-1&quot;&gt;Relevant tickets&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/venantius/yagni/issues/37&quot;&gt;Reader conditionals&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/venantius/yagni/issues/36&quot;&gt;Spec usage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;project-kibit&quot;&gt;Project: &lt;a href=&quot;https://github.com/jonase/kibit&quot;&gt;kibit&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lein-kibit&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;kibit is an analyzer which uses core.logic to search for patterns in code
which can be written to be more idiomatic. Like many other tools on this list,
kibit struggles with parsing some Clojure, like nested requires and reader
conditionals.&lt;/p&gt;

&lt;h4 id=&quot;relevant-tickets-2&quot;&gt;Relevant tickets&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jonase/kibit/issues/202&quot;&gt;Nested requires&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jonase/kibit/pull/194&quot;&gt;Reader conditionals&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;project-lein-bikeshed&quot;&gt;Project: &lt;a href=&quot;https://github.com/dakrone/lein-bikeshed&quot;&gt;lein-bikeshed&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lein-bikeshed&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.4.1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;lein-bikeshed is one of the few tools which worked out of the box.
Unfortunately, the output wasn’t terribly useful. The two issues reported were
that some lines are longer than 80 characters and some functions missing doc
strings. I think, given its name, that lein-bikeshed doesn’t take itself too
seriously in providing the most practical tooling for CI integration, but it
certainly gets points for not choking on the code.&lt;/p&gt;

&lt;h3 id=&quot;project-slamhound&quot;&gt;Project: &lt;a href=&quot;https://github.com/technomancy/slamhound&quot;&gt;slamhound&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slamhound&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.5.5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By only comparing the descriptions, slamhound seemed like one of the most useful
ones of the bunch. It has the ability to clean up namespace aliases, add
requires, and even remove unused requires.&lt;/p&gt;

&lt;p&gt;Unfortunately, it hasn’t been maintained for a year and has started sprouting
various issues and PRs which go unloved. More importantly, it fails to parse
aliased keywords, as well as the parsing of dotted namespace aliases.&lt;/p&gt;

&lt;h4 id=&quot;relevant-tickets-3&quot;&gt;Relevant tickets&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/technomancy/slamhound/issues/79&quot;&gt;Aliased keywords&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/technomancy/slamhound/pull/87&quot;&gt;Dotted namespace alias&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;project-spectrum&quot;&gt;Project: &lt;a href=&quot;https://github.com/arohner/spectrum&quot;&gt;spectrum&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Spectrum is the one on this list which is most exciting for use with CI. It
performs static analysis and, using both specs and type inference, determines if
code is incorrect without running it. Given how robust specs can be, reaching
into the land of dependent types, this is a very promising avenue.
Unfortunately, after speaking with the developer for a bit, it’s clear that
spectrum isn’t yet ready to use.&lt;/p&gt;

&lt;p&gt;In preparation for it, &lt;a href=&quot;/2017/05/31/clojure-spec/&quot;&gt;spec your
functions!&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;project-lein-nvd&quot;&gt;Project: &lt;a href=&quot;https://github.com/rm-hull/lein-nvd&quot;&gt;lein-nvd&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lein-nvd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.3.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;This tool is a must.&lt;/em&gt; Furthermore, it worked without issue. It crawls through a
project’s dependencies and checks for vulnerable versions of libraries. If
there’s a known vulnerability in one of your dependencies, it informs you and
also conveys the severity of the issue. I don’t see why a Clojure back-end would
run CI without this.&lt;/p&gt;

&lt;h3 id=&quot;project-orchestra&quot;&gt;Project: &lt;a href=&quot;https://github.com/jeaye/orchestra&quot;&gt;orchestra&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orchestra&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2017.08.13&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Orchestra is a Clojure library made as a drop-in replacement for
clojure.spec.test.alpha, which provides custom instrumentation that validates
all aspects of function specs. Best of all, it works out of the box on this
source code and can be used during testing, development, and even production.&lt;/p&gt;

&lt;h3 id=&quot;project-cloverage&quot;&gt;Project: &lt;a href=&quot;https://github.com/cloverage/cloverage&quot;&gt;cloverage&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lein-cloverage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0.9&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve &lt;a href=&quot;/2016/12/29/clojure-test-coverage/&quot;&gt;written about cloverage
before&lt;/a&gt; and I’ll
recommend it again. cloverage will report how much coverage your tests have by
instrumenting the code to see which functions are called, which branches are
taken, etc. It works out of the box with this code base and integrates very
nicely into a CI setup.&lt;/p&gt;

&lt;h2 id=&quot;striking-similarities&quot;&gt;Striking similarities&lt;/h2&gt;
&lt;p&gt;There are some promising tools out there, just in the Clojure JVM world, for
verifying code quality and correctness, automatically making improvements, or
even just suggesting cleaner idioms. It seems like most of them, in fact, are
struggling with parsing Clojure code, be it reader conditionals, namespace
aliases, nested requires, or aliased keywords. Given that this is a consistent
problem across several projects and several authors, there is an indication of
insufficient or unreliable tooling when it comes to statically reading Clojure
code. This is likely due to Clojure’s dynamic nature, but, nevertheless, there’s
an untapped market here.&lt;/p&gt;

&lt;p&gt;Given all of the tickets linked above, I encourage readers to try out these
projects on their code bases, issue more tickets, ping maintainers, and
contribute some PRs. Lastly, if there’s a project that should be on this list,
email me!&lt;/p&gt;</content><author><name>jeaye</name></author><category term="clojure" /><category term="spec" /><category term="orchestra" /><category term="survey" /><category term="continuous-integration" /><category term="safety" /><category term="review" /><summary type="html">There are several projects for verifying quality and correctness in Clojure code bases. When surveying how they’ll work on a distributed Clojure server built for Heroku &amp;amp; Postgres, I took some notes on the issues that came up. These notes represent the current state of things, as well as how we, the Clojure community, can help move them forward.</summary></entry><entry><title type="html">NixOS: A lasting impression</title><link href="https://blog.jeaye.com//2017/07/30/nixos-revisited/" rel="alternate" type="text/html" title="NixOS: A lasting impression" /><published>2017-07-30T00:00:00-07:00</published><updated>2017-07-30T00:00:00-07:00</updated><id>https://blog.jeaye.com//2017/07/30/nixos-revisited</id><content type="html" xml:base="https://blog.jeaye.com//2017/07/30/nixos-revisited/">&lt;p&gt;Two years ago, I wrote about &lt;a href=&quot;/2015/11/24/nixos/&quot;&gt;my first impression of NixOS&lt;/a&gt;,
as I was using it in my workstation. While I adored the concept of declarative
OS configuration, it didn’t quite fit the workflow I had in mind for my laptop.
The post was concluded with me considering NixOS for my VPS, but I didn’t quite
want to move away from DigitalOcean, which has support for only a few distros.
What follows details how I’ve been running NixOS since, what it took, and what
I’ve learned.&lt;/p&gt;

&lt;h3 id=&quot;dealing-with-digitalocean&quot;&gt;Dealing with DigitalOcean&lt;/h3&gt;
&lt;p&gt;My VPS is hosting this blog, &lt;a href=&quot;https://jeaye.com/&quot;&gt;jeaye.com&lt;/a&gt;, and many other
sites and services. For this, I use DigitalOcean. Ever since I started with
DigitalOcean a few years ago, they’ve been joy to work with, so I really didn’t
want to leave. Alas, they don’t support many distros, and certainly not custom
ISOs. The first droplet I was administrating was already going against the
grain, running Arch using &lt;a href=&quot;https://github.com/gh2o/digitalocean-debian-to-arch&quot;&gt;a script which converts Debian to Arch in
place&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“What a neat idea,”&lt;/em&gt; I thought, &lt;em&gt;“to trick DigitalOcean into thinking it’s
running a supported distro.”&lt;/em&gt; A couple weeks later,
&lt;a href=&quot;https://github.com/jeaye/nixos-in-place&quot;&gt;nixos-in-place&lt;/a&gt; was born; it remains
the most stable solution for converting any running GNU/Linux setup to NixOS in
place.&lt;/p&gt;

&lt;h3 id=&quot;setting-up-complex-services&quot;&gt;Setting up complex services&lt;/h3&gt;
&lt;p&gt;Once NixOS was running on my DigitalOcean droplet, I had the work of porting all
of the services I was running on my Arch droplet to a declarative setup which I
could easily version with git. Here are some of the services I’m running, as
well as the related configs for each.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeaye/nix-files/blob/e3bf921a5af925465d8f41ec006c87c8f0ffafe3/service/httpd.nix&quot;&gt;apache-httpd&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeaye/nix-files/blob/e3bf921a5af925465d8f41ec006c87c8f0ffafe3/service/fail2ban.nix&quot;&gt;fail2ban&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeaye/nix-files/blob/e3bf921a5af925465d8f41ec006c87c8f0ffafe3/service/dovecot.nix&quot;&gt;dovecot&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeaye/nix-files/blob/e3bf921a5af925465d8f41ec006c87c8f0ffafe3/service/postfix.nix&quot;&gt;postfix&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/jeaye/nix-files/blob/e3bf921a5af925465d8f41ec006c87c8f0ffafe3/service/radicale.nix&quot;&gt;radicale&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the most part, setting up a service on NixOS is similar to setting it up on
any normal distro. The difference is that one generally is limited to the API
provided by that NixOS service. Unlike packages, in Nix and NixOS, services
can’t be overridden. This has only been an issue once, in the past couple
years, but it’s currently limiting my ability to configure
&lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/mail/spamassassin.nix&quot;&gt;spamassassin&lt;/a&gt;
(&lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; has a much better interface than &lt;code class=&quot;highlighter-rouge&quot;&gt;17.03&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;I tend to forget things, like what I’ve set up on a machine, or everything that
was required to get a service running, so having it all in plain text, and
version control, in a reproducible fashion, is ideal.&lt;/p&gt;

&lt;h3 id=&quot;managing-user-homes&quot;&gt;Managing user homes&lt;/h3&gt;
&lt;p&gt;NixOS doesn’t provide a way to declaratively manage user homes. In fact, the
only direct control it provides, declaratively, is over what’s in &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc&lt;/code&gt; and its
subdirectories. There have been some approaches and discussions (like
&lt;a href=&quot;https://github.com/NixOS/nixpkgs/issues/1750&quot;&gt;here&lt;/a&gt; and
&lt;a href=&quot;https://github.com/NixOS/nixpkgs/pull/9250&quot;&gt;here&lt;/a&gt;), but I opted for a much
simpler solution, which was already supported at the time. NixOS forfeits the
conventional ideas of where programs live and how they’re installed and
upgraded, so why not take that liberty with user homes?&lt;/p&gt;

&lt;p&gt;So, since NixOS provides declarative management of &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc&lt;/code&gt; and its subdirectories, I just use &lt;code class=&quot;highlighter-rouge&quot;&gt;/etc/user&lt;/code&gt; as my analogous &lt;code class=&quot;highlighter-rouge&quot;&gt;/home&lt;/code&gt;. For example, the user &lt;code class=&quot;highlighter-rouge&quot;&gt;jeaye&lt;/code&gt;, &lt;a href=&quot;https://github.com/jeaye/nix-files/blob/e3bf921a5af925465d8f41ec006c87c8f0ffafe3/user/jeaye.nix&quot;&gt;defined here&lt;/a&gt;, is described as:&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;jeaye&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;isNormalUser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;home&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/user/jeaye&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;createHome&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;extraGroups&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;wheel&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If I want to put anything in the home directory for &lt;code class=&quot;highlighter-rouge&quot;&gt;jeaye&lt;/code&gt;, I can do so
declaratively, like so:&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;etc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;user/jeaye/.procmailrc&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      INCLUDERC=/etc/user/jeaye/.procmail/list.rc&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      INCLUDERC=/etc/user/jeaye/.procmail/work.rc&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      INCLUDERC=/etc/user/jeaye/.procmail/admin.rc&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;    ''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also use a trick to ensure directories exist, which just involves
declaratively putting a hidden file in there. NixOS will create any parent
directories needed.&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Ensure that the /etc/user/safepaste/paste directory exists&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;etc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;user/safepaste/paste/.manage-directory&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;sticking-with-mainstream-configuration-to-avoid-compilations&quot;&gt;Sticking with mainstream configuration to avoid compilations&lt;/h3&gt;
&lt;p&gt;Initially, I was amazed that my VPS was regularly spending an hour compiling
&lt;a href=&quot;https://en.wikipedia.org/wiki/OpenJDK&quot;&gt;OpenJDK&lt;/a&gt; every time I updated. After
further investigation, in the helpful &lt;code class=&quot;highlighter-rouge&quot;&gt;#nixos&lt;/code&gt; IRC channel on Freenode, it seems
this is because I had disabled X everywhere I could. On a headless server, this
seemed intuitive. Unfortunately, &lt;a href=&quot;https://nixos.org/hydra/&quot;&gt;NixOS Hydra&lt;/a&gt;,
which builds all of NixOS’ deterministic binaries, only builds with so many
configurations. As one can imagine, each new configuration added for a build,
with the various platforms, architectures, and other configurations, expands the
build time exponentially. As such, the VPS now runs with &lt;a href=&quot;https://github.com/jeaye/nix-files/blob/e3bf921a5af925465d8f41ec006c87c8f0ffafe3/system/environment.nix#L26&quot;&gt;environment.noXlibs
set to
false&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;hitting-the-network-in-an-activation-script&quot;&gt;Hitting the network in an activation script&lt;/h3&gt;
&lt;p&gt;I had an upgrade script, which I was running in &lt;code class=&quot;highlighter-rouge&quot;&gt;system.activationScripts&lt;/code&gt;, that
hit the network to check for upgrades. While the machine was already running,
this posed no problem at all and worked quite nicely. Whenever I would
&lt;code class=&quot;highlighter-rouge&quot;&gt;nixos-rebuild switch&lt;/code&gt;, the activation script would run and upgrade the package
if needed. Alas, during a routine reboot, I was no longer able to boot even into
a shell; the kernel would panic. After several hours of painstaking debugging
with a custom initrd, it turns out the problem was that the activation script
hitting the network was failing, since there was no network, and the rest of the
boot would then fail.&lt;/p&gt;

&lt;p&gt;In short, leave network IO out of activation scripts; I’m using a cron job, for
this task, instead. Simple enough.&lt;/p&gt;

&lt;h3 id=&quot;building-clojure-packages-with-leiningen&quot;&gt;Building Clojure packages with Leiningen&lt;/h3&gt;
&lt;p&gt;When bringing in some of my Clojure services, there were issues with compiling
Leiningen projects, due to the dependency downloading. By default, the home of
the Nix builder isn’t writable, so some workarounds are needed. This setup has
been working for me (as part of your typical Nix package):&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;buildInputs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;leiningen&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;buildPhase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  # For leiningen&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  export HOME=$PWD&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  export LEIN_HOME=$HOME/.lein&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  mkdir -p $LEIN_HOME&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  echo &quot;{:user {:local-repo &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;$LEIN_HOME&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}}&quot; &amp;gt; $LEIN_HOME/profiles.clj&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;leiningen&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/bin/lein uberjar&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;pulling-from-unstable-when-packages-are-too-old&quot;&gt;Pulling from unstable when packages are too old&lt;/h3&gt;
&lt;p&gt;Occasionally, a package in the Nix repos for a given release, like &lt;code class=&quot;highlighter-rouge&quot;&gt;17.03&lt;/code&gt;, will be too old, have a bug, etc. If NixOS &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; has a fix for this, it might be worthwhile to bring in the &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; version of just that package, not your whole OS. Due to the elegance of Nix’s dependency management, this isn’t a problem at all; the &lt;code class=&quot;highlighter-rouge&quot;&gt;unstable&lt;/code&gt; channel follows each successful build of the &lt;code class=&quot;highlighter-rouge&quot;&gt;master&lt;/code&gt; branch, so we can just pull that in. Say we wanted to do this for &lt;a href=&quot;https://weechat.org/&quot;&gt;weechat&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;systemPackages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pkgsUnstable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;import&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;kr&quot;&gt;fetchTarball&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;https://github.com/NixOS/nixpkgs-channels/archive/nixos-unstable.tar.gz&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;in&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;pkgsUnstable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;weechat&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the next version has been released, or the fix has been added to your
default channel, then this can go back to normal.&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;systemPackages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;pkgs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;weechat&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;minimizing-used-disk-space&quot;&gt;Minimizing used disk space&lt;/h3&gt;
&lt;p&gt;If left on its own, NixOS can be quite greedy with disk space. This is primarily
a trade-off for the convenience of a purely functional package infrastructure,
but it’s still worth noting how it can be managed. The following bits have
helped keep my system pretty lean (&lt;code class=&quot;highlighter-rouge&quot;&gt;/nix&lt;/code&gt; is 5.4G).&lt;/p&gt;

&lt;div class=&quot;language-nix highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Auto GC every morning&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;nix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;gc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;automatic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;systemCronJobs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0 3 * * * root /etc/admin/optimize-nix&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;etc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;admin/optimize-nix&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      #!/run/current-system/sw/bin/bash&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      set -eu&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      # Delete everything from this profile that isn't currently needed&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      nix-env --delete-generations old&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      # Delete generations older than a week&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      nix-collect-garbage&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      nix-collect-garbage --delete-older-than 7d&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      # Optimize&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      nix-store --gc --print-dead&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;      nix-store --optimise&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;    ''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;mode&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;0774&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;various-nitpicks-which-could-improve-the-nixos-users-experience&quot;&gt;Various nitpicks which could improve the NixOS user’s experience&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Calculating SHA-256 of a package isn’t easy&lt;/p&gt;

    &lt;p&gt;It seems like the best way to do this is to put in a bad SHA-256, try to
  build, have it fail and tell you the correct one, and then put it in.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Nix command-line UI needs a re-design&lt;/p&gt;

    &lt;p&gt;This is a &lt;a href=&quot;https://github.com/NixOS/nix/issues/779&quot;&gt;known issue&lt;/a&gt; but, after
  nearly two years, has not yet been merged. In short, commands like &lt;code class=&quot;highlighter-rouge&quot;&gt;nix-env
  -qa&lt;/code&gt; would become &lt;code class=&quot;highlighter-rouge&quot;&gt;nix search&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;nix-env -qc&lt;/code&gt; would become &lt;code class=&quot;highlighter-rouge&quot;&gt;nix status&lt;/code&gt;.
  There’s no reason users should have to remember, or learn, the former.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Editor support for Nix and Nixpkgs&lt;/p&gt;

    &lt;p&gt;Yes, there are Nix plugins for every good editor. What I haven’t seen any
  formal discussion about, but could help Nix along, is a more functional
  integration into those editors. I think &lt;a href=&quot;https://github.com/matthewbauer/nix-mode&quot;&gt;Emacs is currently best
  posed&lt;/a&gt;, in this regard, but
  providing semantic completion of all the items in
  &lt;a href=&quot;https://github.com/NixOS/nixpkgs&quot;&gt;nixpkgs&lt;/a&gt;, completion for dependency
  injection and Nix’s standard library, semantic highlighting, linting,
  SHA-256 calculation (when writing packages), etc., might really help users
  jump into the Nix world.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Declaring private data&lt;/p&gt;

    &lt;p&gt;This is something I’ve yet to tackle. Instead, there are various places within
  my NixOS files which are marked as &lt;code class=&quot;highlighter-rouge&quot;&gt;XXX&lt;/code&gt;, with a comment saying what I need to
  do. These comments represent imperative steps I need to take when deploying this
  configuration to a new machine. Currently, this just entails setting up private
  keys, &lt;a href=&quot;https://en.wikipedia.org/wiki/.htpasswd&quot;&gt;htpassword&lt;/a&gt; files, and some
  private git repos which I host. A possible solution for passwords, and the like,
  may be to commit them to git after GPG-encrypting them. For everything else,
  perhaps a GPG-encrypted bash script in the repo, which does the remaining setup
  interactively, would suffice.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;considering-whats-left-and-how-things-are&quot;&gt;Considering what’s left and how things are&lt;/h3&gt;
&lt;p&gt;It’s been two years with NixOS on my VPS and they’ve been great. My biggest
complaints are in the form of the Nix expression language itself not being very
easy to use, having a good standard library, and having much documentation on
doing generic tasks. In this regard, I think that
&lt;a href=&quot;https://www.gnu.org/software/guix/&quot;&gt;GuixSD&lt;/a&gt; is much more appealing: it uses
&lt;a href=&quot;https://www.gnu.org/software/guile/&quot;&gt;Guile Scheme&lt;/a&gt;, which has clear practical
applications and is a much more general-purpose language that system
administrators might even already know.&lt;/p&gt;

&lt;p&gt;Guix’s stance on free software, due to it being a GNU project, is also more
appealing; my VPS has absolutely no need for proprietary software (it’s only
somewhat harder to argue that for my workstation).&lt;/p&gt;

&lt;p&gt;I very much plan to keep NixOS running on my VPS and switching as much as I can
to the declarative style. After having such great success in the server world,
I’ve been thinking more about trying it again for my workstation. Alas, I think
my issues would the Nix language would bug me enough to where that wouldn’t be
enjoyable. If I can work out getting Skype (and maybe nVidia) on GuixSD, or
maybe if &lt;a href=&quot;https://ring.cx/en&quot;&gt;GNU Ring&lt;/a&gt; stabilizes enough, then I’d really enjoy
having a declarative workspace in very much the same fashion. Maybe in two years
I’ll be following up with my thoughts on that.&lt;/p&gt;</content><author><name>jeaye</name></author><category term="linux" /><category term="nixos" /><category term="nix" /><category term="review" /><category term="digital ocean" /><category term="functional programming" /><summary type="html">Two years ago, I wrote about my first impression of NixOS, as I was using it in my workstation. While I adored the concept of declarative OS configuration, it didn’t quite fit the workflow I had in mind for my laptop. The post was concluded with me considering NixOS for my VPS, but I didn’t quite want to move away from DigitalOcean, which has support for only a few distros. What follows details how I’ve been running NixOS since, what it took, and what I’ve learned.</summary></entry><entry><title type="html">Porting Orchestra to ClojureScript</title><link href="https://blog.jeaye.com//2017/06/30/orchestra-cljs/" rel="alternate" type="text/html" title="Porting Orchestra to ClojureScript" /><published>2017-06-30T00:00:00-07:00</published><updated>2017-06-30T00:00:00-07:00</updated><id>https://blog.jeaye.com//2017/06/30/orchestra-cljs</id><content type="html" xml:base="https://blog.jeaye.com//2017/06/30/orchestra-cljs/">&lt;p&gt;&lt;a href=&quot;https://github.com/jeaye/orchestra&quot;&gt;Orchestra&lt;/a&gt; is a Clojure library made as a
drop-in replacement for &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.spec.test.alpha&lt;/code&gt;, which provides custom
instrumentation that validates all aspects of function specs. Now, it also works
with ClojureScript. This post covers some pitfalls of porting Clojure projects
to ClojureScript, as well as some instruction for those looking to do so while
maintaining a respectable amount of sanity.&lt;/p&gt;

&lt;h3 id=&quot;consideration-directory-structure&quot;&gt;Consideration: Directory structure&lt;/h3&gt;
&lt;p&gt;For sake of illustration, imagine you’re porting a hypothetical &lt;code class=&quot;highlighter-rouge&quot;&gt;kitty-ninja&lt;/code&gt;
Clojure project to ClojureScript. Your directory structure almost certainly
looks like this:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── project.clj
├── src
│   └── kitty_ninja
│       ├── core.clj
│       └── meow.clj
└── test
    └── kitty_ninja
        ├── core_test.clj
        └── meow_test.clj
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are three types of Clojure files to deal with, when supporting both
Clojure and ClojureScript. There’s the obvious &lt;code class=&quot;highlighter-rouge&quot;&gt;clj&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;cljs&lt;/code&gt;, but also
&lt;code class=&quot;highlighter-rouge&quot;&gt;cljc&lt;/code&gt;, which represents a file which may be compiled as either Clojure or
ClojureScript. When refactoring your shared code from Clojure land to be exposed
to ClojureScript, &lt;code class=&quot;highlighter-rouge&quot;&gt;cljc&lt;/code&gt; is what will be used. For the most part, code in &lt;code class=&quot;highlighter-rouge&quot;&gt;cljc&lt;/code&gt;
files can either be Clojure or ClojureScript, but platform-specific bits can
also be included using &lt;a href=&quot;https://clojure.org/guides/reader_conditionals&quot;&gt;reader
conditionals&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The directory structure is thus changed to something like this:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── project.clj
├── src
│   ├── clj
│   │   └── kitty_ninja
│   │       └── core.clj
│   ├── cljc
│   │   └── kitty_ninja
│   │       └── meow.cljc
│   └── cljs
│       └── kitty_ninja_cljs
│           └── core.cljs
└── test
    ├── clj
    │   └── kitty_ninja
    │       └── core_test.clj
    ├── cljc
    │   └── kitty_ninja
    │       └── meow_test.cljc
    └── cljs
        └── kitty_ninja_cljs
            └── core_test.cljs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As is shown, the &lt;code class=&quot;highlighter-rouge&quot;&gt;meow&lt;/code&gt; functionality is shared between both Clojure and
ClojureScript. Furthermore, its tests are also shared. Aside from that, both
Clojure and ClojureScript have their own platform-specific code and tests/entry
points as well. More on this later.&lt;/p&gt;

&lt;h3 id=&quot;pitfall-namespaces&quot;&gt;Pitfall: Namespaces&lt;/h3&gt;
&lt;p&gt;The likely first thought, when looking to port the &lt;code class=&quot;highlighter-rouge&quot;&gt;kitty-ninja&lt;/code&gt; project to
ClojureScript, would be to also have the ClojureScript counterpart be in the
&lt;code class=&quot;highlighter-rouge&quot;&gt;kitty-ninja&lt;/code&gt; namespace.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This will cause much, much more harm than good.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For this reason, we have &lt;code class=&quot;highlighter-rouge&quot;&gt;cljs.spec&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;clojure.spec&lt;/code&gt; for
ClojureScript. Similarly, we now have &lt;code class=&quot;highlighter-rouge&quot;&gt;orchestra-cljs.spec.test&lt;/code&gt; instead of
&lt;code class=&quot;highlighter-rouge&quot;&gt;orchestra.spec.test&lt;/code&gt;. Since ClojureScript can actually require Clojure files,
and all of its macros are run in Clojure, as well as the class path issues that
arise with having &lt;code class=&quot;highlighter-rouge&quot;&gt;src/clj/&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;src/cljs/&lt;/code&gt; in your Leiningen &lt;code class=&quot;highlighter-rouge&quot;&gt;:source-paths&lt;/code&gt;,
just agree from the start that your ClojureScript port will be in a different
namespace.&lt;/p&gt;

&lt;h3 id=&quot;pitfall-macros&quot;&gt;Pitfall: Macros&lt;/h3&gt;
&lt;p&gt;In ClojureScript, macro expansion is a very distinct phase of compilation, which
involves running Clojure on the ClojureScript code. As such, macros need to be
in either a &lt;code class=&quot;highlighter-rouge&quot;&gt;clj&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;cljc&lt;/code&gt; file. Part of refactoring &lt;code class=&quot;highlighter-rouge&quot;&gt;kitty-ninja&lt;/code&gt;, or any
other project, would involve moving the macros which will be shared with
ClojureScript to the &lt;code class=&quot;highlighter-rouge&quot;&gt;src/cljc/&lt;/code&gt; directory. Furthermore, any
ClojureScript-specific macros may be kept in the &lt;code class=&quot;highlighter-rouge&quot;&gt;src/cljs/&lt;/code&gt; directory. This may
be counter-intuitive, since it means there will be &lt;code class=&quot;highlighter-rouge&quot;&gt;cljc&lt;/code&gt; files in &lt;code class=&quot;highlighter-rouge&quot;&gt;src/cljs/&lt;/code&gt;,
but this makes sense in the case where they’re for ClojureScript-only macros.&lt;/p&gt;

&lt;p&gt;There is also a pattern, which has largely gone unstated as far I can tell,
where a &lt;code class=&quot;highlighter-rouge&quot;&gt;cljs&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;cljc&lt;/code&gt; file can have the same file name and namespace.  If
the &lt;code class=&quot;highlighter-rouge&quot;&gt;cljs&lt;/code&gt; file requires the &lt;code class=&quot;highlighter-rouge&quot;&gt;cljc&lt;/code&gt; version, to bring in its macros, then
they’ll automatically be available to anyone to requires the &lt;code class=&quot;highlighter-rouge&quot;&gt;cljs&lt;/code&gt; file. Here’s
an illustration of how that works:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
└── src
    └── cljs
        └── kitty_ninja_cljs
            ├── core.cljs
            ├── stealth.cljc
            └── stealth.cljs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;stealth.cljc&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kitty-ninja-cljs.stealth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defmacro&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;purr&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;purrrr&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;stealth.cljs&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kitty-ninja-cljs.stealth&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:require-macros&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kitty-ninja-cljs.stealth&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;core.cljs:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kitty-ninja-cljs.core&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kitty-ninja-cljs.stealth&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;st&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;st/purr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Macro invocation without having to do require-macros&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;pitfall-cljsbuild-profiles&quot;&gt;Pitfall: Cljsbuild profiles&lt;/h3&gt;
&lt;p&gt;Unlike &lt;a href=&quot;https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md&quot;&gt;Leiningen
profiles&lt;/a&gt;,
which are quite flexible,
&lt;a href=&quot;https://github.com/emezeske/lein-cljsbuild&quot;&gt;cljsbuild&lt;/a&gt; profiles tend toward
redundancy. Furthermore, cljsbuild requires a top-level key in the
&lt;code class=&quot;highlighter-rouge&quot;&gt;project.clj&lt;/code&gt;. As a result, the best setup I’ve found is to provide the base
information at the top-level, using a fixed build name, rather than in a shared
Leiningen profile. From there, use Leiningen profiles to customize the values
you need. Here’s an annotated example from Orchestra:&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;defproject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; ... elided a great deal ...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Keep cljs in here so `lein jar` packages the sources&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:source-paths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;src/clj/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;src/cljs/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Base cljs setup&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:cljsbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:test-commands&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;lein&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;doo&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;app&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;once&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:builds&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                       &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:source-paths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;src/cljs/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:compiler&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:optimizations&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:advanced&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:pretty-print&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:parallel-build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:output-dir&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;target/test&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                         &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:output-to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;target/test.js&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

  &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; Custom dev dependencies, sources, and entry point for test running&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:profiles&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:dev&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:dependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lein-doo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.1.7&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:source-paths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test/clj/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test/cljc/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                   &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:cljsbuild&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:builds&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:source-paths&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test/cljs/&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;test/cljc/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                         &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:compiler&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                         &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:main&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;orchestra-cljs.test&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                          &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:target&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:nodejs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}}}}})&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;consideration-tests-with-doo&quot;&gt;Consideration: Tests with doo&lt;/h3&gt;
&lt;p&gt;A combination of &lt;a href=&quot;https://github.com/bensu/doo&quot;&gt;doo&lt;/a&gt; and ClojureScript’s
&lt;code class=&quot;highlighter-rouge&quot;&gt;cljs.test&lt;/code&gt; will allow for mostly painless sharing of tests between Clojure and
ClojureScript. A word of caution, however, based on my experience:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use Node for your tests, not Phantom.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There exists an &lt;a href=&quot;https://github.com/bensu/doo/issues/135&quot;&gt;issue on doo&lt;/a&gt; for
this, but PhantomJS is not as well supported and, in my experience, not nearly
as reliable. Fortunately, switching to Node should be as easy as adding
&lt;code class=&quot;highlighter-rouge&quot;&gt;:target :nodejs&lt;/code&gt; to the cljsbuild profile and changing the &lt;code class=&quot;highlighter-rouge&quot;&gt;lein doo&lt;/code&gt; command
to use &lt;code class=&quot;highlighter-rouge&quot;&gt;node&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;phantom&lt;/code&gt;. See the Orchestra profile above for
reference.&lt;/p&gt;

&lt;h3 id=&quot;pitfall-figwheels-repl&quot;&gt;Pitfall: Figwheel’s REPL&lt;/h3&gt;
&lt;p&gt;If you end up using &lt;a href=&quot;https://github.com/bhauman/lein-figwheel&quot;&gt;figwheel&lt;/a&gt;,
which arguably most non-trivial ClojureScript projects do, absolutely consider
using &lt;a href=&quot;https://github.com/hanslub42/rlwrap&quot;&gt;rlwrap&lt;/a&gt; (available on most Unix-like
platforms). By default, fighwheel’s REPL doesn’t provide history (up and down
arrows), word/line based editing (like &lt;code class=&quot;highlighter-rouge&quot;&gt;^W&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;^U&lt;/code&gt;), or reverse search of
previous inputs (using &lt;code class=&quot;highlighter-rouge&quot;&gt;^R&lt;/code&gt;). rlwrap will provide all of these otherwise
ubiquitous features for you, without breaking any figwheel functionality.&lt;/p&gt;

&lt;h3 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h3&gt;
&lt;p&gt;ClojureScript’s tooling doesn’t compare to Clojure’s, but it’s certainly a
capable platform. For those already working in ClojureScript, be it with
React(Native) or anything else, please do consider using spec and
instrumentation to aid in your development. For those coming to ClojureScript,
from Clojure, hopefully these points will save you some time.&lt;/p&gt;</content><author><name>jeaye</name></author><category term="clojure" /><category term="clojurescript" /><category term="spec" /><category term="orchestra" /><category term="dependent types" /><category term="safety" /><summary type="html">Orchestra is a Clojure library made as a drop-in replacement for clojure.spec.test.alpha, which provides custom instrumentation that validates all aspects of function specs. Now, it also works with ClojureScript. This post covers some pitfalls of porting Clojure projects to ClojureScript, as well as some instruction for those looking to do so while maintaining a respectable amount of sanity.</summary></entry><entry><title type="html">Dear Clojure devs, use clojure.spec please</title><link href="https://blog.jeaye.com//2017/05/31/clojure-spec/" rel="alternate" type="text/html" title="Dear Clojure devs, use clojure.spec please" /><published>2017-05-31T00:00:00-07:00</published><updated>2017-05-31T00:00:00-07:00</updated><id>https://blog.jeaye.com//2017/05/31/clojure-spec</id><content type="html" xml:base="https://blog.jeaye.com//2017/05/31/clojure-spec/">&lt;p&gt;Clojure 1.9 has introduced a novel library,
&lt;a href=&quot;https://clojure.org/about/spec&quot;&gt;clojure.spec&lt;/a&gt;, which allows developers to parse
and validate data using a predicative API which is just composed of Clojure
functions. If there’s one thing Clojure’s good at, though there’s not just one,
it’s the &lt;a href=&quot;https://en.wikipedia.org/wiki/Pure_function&quot;&gt;pure&lt;/a&gt; transformation of
data. With spec, that grasp is broadened to allow for exceedingly expressive
validation of data as well. What’s most important is that Clojure developers
understand this new tool and use it to improve the safety and readability of
their code.&lt;/p&gt;

&lt;h3 id=&quot;learning-clojurespec&quot;&gt;Learning clojure.spec&lt;/h3&gt;
&lt;p&gt;It’s been around long enough for there to be some great introductory material.
There’s an official &lt;a href=&quot;https://clojure.org/about/spec&quot;&gt;Rationale and Overview&lt;/a&gt;,
thorough &lt;a href=&quot;https://clojure.org/guides/spec&quot;&gt;Spec Guide&lt;/a&gt;, more illustrative &lt;a href=&quot;https://lambdaisland.com/episodes/clojure-spec&quot;&gt;Video
Introduction&lt;/a&gt;, etc. This post
assumes a working understanding of spec, since its goal is to talk more about
spec’s practical applications.&lt;/p&gt;

&lt;h3 id=&quot;why-spec&quot;&gt;Why spec?&lt;/h3&gt;
&lt;p&gt;Knowing how spec works and even how to use it is handy. Still, why bother
spec’ing? If you look at the spec docs linked above, most reasons are for the
aid in &lt;a href=&quot;https://en.wikipedia.org/wiki/Software_testing&quot;&gt;testing&lt;/a&gt;, specifically
with &lt;a href=&quot;https://clojure.org/guides/spec#_generators&quot;&gt;generative testing&lt;/a&gt;. That’s
great, if you like writing tests, or if your tests aren’t already written in
something else. What if that’s not the case?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Please. Still use clojure.spec.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here’s why.&lt;/p&gt;

&lt;h3 id=&quot;instrumentation&quot;&gt;Instrumentation&lt;/h3&gt;
&lt;p&gt;A hugely overlooked aspect of spec is its
&lt;a href=&quot;https://clojure.org/guides/spec#_instrumentation_and_testing&quot;&gt;instrumentation&lt;/a&gt;.
In short, if you spec your functions and your data, you can ask Clojure to
automatically check every single function call to make sure the arguments are
correct. Not just during testing, but during development or even production.
Furthermore, if you use &lt;a href=&quot;https://github.com/jeaye/orchestra&quot;&gt;Orchestra&lt;/a&gt;, then
Clojure can automatically check every function’s return value against its spec,
among other things. Note, this includes ClojureScript!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is superb.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Coming from C++, or Java, or C#, or so many other languages: have you been able
to easily instrument every single function to validate it’s working properly?
Not just with static types, which can be quite limiting, but with arbitrary
predicates written in the same language you’re using? Unlikely.&lt;/p&gt;

&lt;p&gt;Let’s look at some code.&lt;/p&gt;

&lt;h4 id=&quot;simple-math&quot;&gt;Simple math&lt;/h4&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a simple function, so it’s easy to tell what &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; needs to be. In more
complex functions, that’s not always the case. Now, what if we were to call this
with something other than a number?&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;my-inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; =&amp;gt; java.lang.NullPointerException&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;my-inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ok&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;; =&amp;gt; java.lang.ClassCastException&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Those are pretty helpful. The exception will include a stack trace, so you can
get the line number and find the right function. Let’s spec out that function
and see what we’d get with instrumentation enabled.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clojure.spec.alpha&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/fdef&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my-inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/cat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just a single example should be descriptive enough, once we &lt;a href=&quot;https://github.com/jeaye/orchestra#usage&quot;&gt;enable
Orchestra&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;my-inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ok&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionInfo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'user/my-inc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;did&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conform&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;In&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ok&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fails&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clojure.spec.alpha/spec&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clojure.spec.alpha/value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ok&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clojure.spec.alpha/args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ok&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clojure.spec.alpha/failure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:instrument&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:orchestra.spec.test/caller&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;form-...&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:line&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:var-scope&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user/eval42203&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s gorgeous. Before we get into the function, we’re stopped with some very
detailed information that &lt;code class=&quot;highlighter-rouge&quot;&gt;[:args :x]&lt;/code&gt; (the argument called &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt;) was supposed to
match &lt;code class=&quot;highlighter-rouge&quot;&gt;number?&lt;/code&gt;, but it has the value of &lt;code class=&quot;highlighter-rouge&quot;&gt;&quot;ok&quot;&lt;/code&gt;. We also get to see all the
other args to the function, line/file info, etc. Compared to typical
statically-typed languages, and the typical Clojure exceptions, which say
“expected number, got string,” we’re now dealing with values, not just types. In
this way, spec behaves more like a &lt;a href=&quot;https://en.wikipedia.org/wiki/Dependent_type&quot;&gt;dependent
type system&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The most important aspect has yet to be mentioned: if a Clojure library provides
these specs for its functions and data, any consumers using Orchestra and spec
will immediately be able to benefit from automatic instrumentation. Each spec is
also a form of documentation which must be up-to-date with the code (or
instrumentation would fail!).&lt;/p&gt;

&lt;h4 id=&quot;more-complex-map-extraction&quot;&gt;More complex map extraction&lt;/h4&gt;
&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;; This behaves similarly to clojure.core/select-keys&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Given a map and some keys, return a map with only those keys&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;some?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/fdef&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/cat&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map?&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;s/coll-of&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;any?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                 &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example, we can use Orchestra to take advantage of spec’s &lt;code class=&quot;highlighter-rouge&quot;&gt;:fn&lt;/code&gt; spec.
These are executed at the end of a function call, and they’re given both the
arguments and the return value. In this case, we can verify that the keys of the
output map are exactly the keys meant to be extracted. Note, this expects that
all keys were present, but that’s the sort of control which you can embed and
automatically run on each function call.&lt;/p&gt;

&lt;p&gt;If we intentionally add a bug, where we use &lt;code class=&quot;highlighter-rouge&quot;&gt;if-let&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;if-some&lt;/code&gt; or
&lt;code class=&quot;highlighter-rouge&quot;&gt;some?&lt;/code&gt;, this function won’t work well with &lt;code class=&quot;highlighter-rouge&quot;&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;defn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Given a map and some keys, return a map with only those keys&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;if-let&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
              &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
          &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With instrumentation enabled, here’s what we might see. Since it’s the &lt;code class=&quot;highlighter-rouge&quot;&gt;:fn&lt;/code&gt;
spec which fails, the error will give us all of the argument values, as well as
the return value. It gives use the predicate which failed and dropping them all
in the REPL would allow us to figure out exactly why.&lt;/p&gt;

&lt;div class=&quot;language-clojure highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user=&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:bar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:spam&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;meow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExceptionInfo&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;'user/extract&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;did&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;conform&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                     &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:bar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:spam&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;meow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fails&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                               &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clojure.spec.alpha/spec&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clojure.spec.alpha/value&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                          &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:bar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:spam&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;meow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                                 &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clojure.spec.alpha/fn&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ret&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                       &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:args&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:m&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:bar&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;false,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:spam&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;meow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                              &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:ks&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:bar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:clojure.spec.alpha/failure&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:instrument&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
               &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:orchestra.spec.test/caller&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;form-...&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:line&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                                            &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;:var-scope&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user/eval53563&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;a-real-world-example&quot;&gt;A real-world example&lt;/h3&gt;
&lt;p&gt;Just recently, I opened a &lt;a href=&quot;https://github.com/reagent-project/reagent/pull/301&quot;&gt;pull request to
Reagent&lt;/a&gt;, which is an
excellent project, in hopes of improving its input validation and error
messages. Rather than using spec, it’s performing manual asserts on input data
and then providing adhoc error messages on failed validation. My PR keeps the
asserts, but refactors them to common helpers and improves the messages
(introducing new deps isn’t typically the right first step in improving a
library as an outsider). Still, we can do so much better than that. These can be
checked for us and we can describe the shape of the data as it should be when it
flows through our Clojure machines.&lt;/p&gt;

&lt;h3 id=&quot;performance-implications&quot;&gt;Performance implications&lt;/h3&gt;
&lt;p&gt;Automatically instrumenting every single function call, checking all the
arguments, return values, and possibly &lt;code class=&quot;highlighter-rouge&quot;&gt;:fn&lt;/code&gt; specs sounds pretty slow, right?
You may be surprised. For development, I’ve seen absolutely no notable
performance issues running Orchestra and instrumenting just about every function
in a back-end deployment of Ring + Compojure + PostgreSQL. Your mileage may
vary, but this is something you should try first, get as much out of it as you
can, and only put down if you absolutely must.&lt;/p&gt;

&lt;p&gt;For the safety of your programs and the programs of everyone using your
libraries, Clojure devs, please spec out your functions and your data. If you
want help writing your specs, heck, email me and let’s get it done. Clojure devs
deserve the huge win of automatic instrumentation.&lt;/p&gt;</content><author><name>jeaye</name></author><category term="clojure" /><category term="spec" /><category term="orchestra" /><category term="rant" /><category term="dependent types" /><category term="safety" /><summary type="html">Clojure 1.9 has introduced a novel library, clojure.spec, which allows developers to parse and validate data using a predicative API which is just composed of Clojure functions. If there’s one thing Clojure’s good at, though there’s not just one, it’s the pure transformation of data. With spec, that grasp is broadened to allow for exceedingly expressive validation of data as well. What’s most important is that Clojure developers understand this new tool and use it to improve the safety and readability of their code.</summary></entry></feed>