Friday, September 11, 2020

Trying TCR

A fellow at the Recurse Center hosted a workshop on TCR (see Kent Beck's post for an introduction to the idea), using these templates

The short of it is: a watcher will automatically run tests upon file save; if any tests fail, it will revert to the prior commit; else, it will make a new commit. 

Despite some initial mental (and practical workflow) resistance, I've warmed very rapidly to the idea. 

By far the biggest change is the revert part of test && commit || revert. Some initial complaints:

  • I made a typo, and it revert to the last commit
  • I just lost a whole block of code
  • I can't just try this simple thing because it keeps failing a test and getting deleted 
The first one maybe retains some legitimacy, but in general, the idea is: 
  • slow down
  • write fragments that you understand
  • write smaller fragments if necessary 
The process also highlights the fact that tests may evolve with the code; as an interface changes, so too do the tests need to change (or TCR won't let you make the commits!).


A few implementation notes for macOS users, building on the Python example from these templates

This gist contains the edits discussed below.

watch.sh
  • Instead of inotifywait, on macOS fswatch can be installed from Homebrew (brew install fswatch)
  • fswatch should be called with the --one-event flag, since it's already called in an infinite loop (otherwise it blocks the next line)
commit.sh
  • By default, TCR will auto-commit with the same message; as one possible alternative, call AppleScript and ask for a commit message via a prompt
    

test.sh
  • The default example has some assertions in the main Python file; modify this to call a testing framework instead (in my case, py.test)


There's a good video here of doing TCR-style testing in Elm. An interesting aspect they mention is squashing a bunch of TCR-generated commits ("what changed") at a later point into more of an explanatory commit ("why the changes").


P.S. for best results with editors, autosave features should be disabled, and autoreload features should be enabled, e.g. in emacs: 

(global-auto-revert-mode t)
(setq auto-save-default nil)