diff --git a/HACKING.md b/HACKING.md
new file mode 100644
index 0000000..da734b7
--- /dev/null
+++ b/HACKING.md
@@ -0,0 +1,60 @@
+# Hacking on/in source
+
+## Setup
+
+### Step 1 - Install bazel
+
+As usual, Ubuntu's packaging of the Bazel bootstrap script is ... considerably stale.
+Add the upstream Bazel ppa so we can get reasonably fresh builds.
+
+``` sh
+sudo apt install apt-transport-https curl gnupg
+curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
+sudo mv bazel.gpg /etc/apt/trusted.gpg.d/
+echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
+```
+
+## Step 2 - Create a canonical `python`
+
+Bazel, sadly, expects that a platform `python` exists for things.
+Due to Bazel's plugins model, changing this is miserable.
+On Archlinux, this isn't a problem. `python` is `python3`. But Ubuntu ... did the other thing.
+
+``` sh
+sudo apt install python-is-python3
+```
+
+### Step 3 - Non-hermetic build deps
+
+The way that Bazel's Python toolchain(s) work is that ultimately they go out to the non-hermetic platform.
+So, in order to be able to use the good tools we have to have some things on the host filesystem.
+
+``` sh
+if [[ "$(sqlite3 --version | awk -F'.' '/^3/ {print $2; exit}')" -lt 35 ]]; then
+  echo "SQLite 3.35.0 or later  (select ... returning support) required"
+  exit 1
+fi
+
+sudo apt install \
+  python3-setuptools \
+  postgresql libpq-dev \
+  sqlite3 libsqlite3-dev
+```
+
+## Working in source
+
+`source activate.sh` is the key.
+It automates a number of tasks -
+1. Building a virtualenv
+2. Synchronizing the virtualenv from the requirements.txt
+3. Updating the virtualenv with all project paths
+4. Activating that virtualenv
+
+`./tools/lint.sh` wraps up various linters into a one-stop shop.
+
+`./tools/fmt.sh` wraps up various formatters into a one-stop shop.
+
+`bazel build ...` will attempt to build everything.
+While with a traditional build system this would be unreasonable, in Bazel which caches build products efficiently it's quite reasonable.
+
+`bazel test ...` likewise will run (or know it doesn't need to run) all the tests.
diff --git a/README.md b/README.md
index a8fe6fc..13c92a8 100644
--- a/README.md
+++ b/README.md
@@ -13,55 +13,7 @@ And so I'm going the other way; Bazel in a monorepo with subprojects so I'm able
 
 ## Hacking (Ubuntu)
 
-### Step 1 - Install bazel
-
-As usual, Ubuntu's packaging of the Bazel bootstrap script is ... considerably stale.
-Add the upstream Bazel ppa so we can get reasonably fresh builds.
-
-``` sh
-sudo apt install apt-transport-https curl gnupg
-curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
-sudo mv bazel.gpg /etc/apt/trusted.gpg.d/
-echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
-```
-
-### Step 2 - Create a canonical `python`
-
-Bazel, sadly, expects that a platform `python` exists for things.
-Due to Bazel's plugins model, changing this is miserable.
-So just create one.
-
-``` sh
-if ! `which python > /dev/null`; then
-  sudo ln -s `which python3` /usr/bin/python
-fi
-```
-
-### Step 3 - Non-hermetic build deps
-
-The way that Bazel's Python toolchain(s) work is that ultimately they go out to the non-hermetic platform.
-So, in order to be able to use the good tools we have to have some things on the host filesystem.
-
-``` sh
-if [[ "$(sqlite3 --version | awk -F'.' '/^3/ {print $2; exit}')" -lt 35 ]]; then
-  echo "SQLite 3.35.0 or later  (select ... returning support) required"
-  exit 1
-fi
-
-sudo apt install \
-  python3-setuptools \
-  postgresql libpq-dev \
-  sqlite3 libsqlite3-dev
-```
-
-### Working in source
-
-`source activate.sh` is the key.
-It automates a number of tasks -
-1. Building a virtualenv
-2. Synchronizing the virtualenv from the requirements.txt
-3. Updating the virtualenv with all project paths
-4. Activating that virtualenv
+See [HACKING.md](HACKING.md)
 
 ## License