Fork me on GitHub
#clojuredesign-podcast
<
2020-11-14
>
nate00:11:54

Happy Friday the 13th! New episode out today, wherein we talk about the times we would rather not be lazy. https://clojuredesign.club/episode/088-do-dorun-run/

🎉 9
Faris09:11:30

3rd Larry Wall virtue is impatience

ianjones14:11:27

@neumann or @nate do y’all have any open source examples of good fiddle files? I understand the concept but wondering what they look like “in production”

nate17:11:30

@ianjones here are a couple of examples: - https://github.com/app-sauce/fiddle-driven-development-2018-talk - source code that accompanied a talk I gave at the LA Clojure meetup. The fiddle files are in dev/fiddle, and the readme shows how to exersize them - https://github.com/jrwdunham/tegere - this is a library maintained by @jrwdunham, and you can see the fiddles in src/tegere/fiddle/ I found the second one with a github code search for fiddle, although that's mostly dominated by mentions of hyperfiddle.

jrwdunham18:11:10

That's funny. I learned the concept of fiddle files from listening to your podcast. Very helpful idea for me — thanks!

jrwdunham18:11:47

I'd be interested to hear your thoughts on the tension between tests and fiddle files (or "Rich" comments) in this context @ianjones. REPL-driven dev seems to encourage fiddling and evaluating as you build, and once you confirm that things work as you expect, testing seems unnecessary. But, in reality, sometimes tests are necessary or mandatory. So then you have to convert your fiddles to tests, and that can be inefficient. Wondering if you have strategies to mitigate this inefficiency, or if you don't really see it as a problem...

neumann21:11:57

@jrwdunham I definitely have parts of a fiddle file that I copy and paste over into a test file. I even have "Rich" comments in test files too. That's kind of a hybrid. I use vim-fireplace which detects test files and re-runs the tests automatically when I reload the namespace for that file. It's a fluid process, but in general, I: • build a "Rich" comment in some source I'm working on • move that comment over to a fiddle file as it grows • move parts of that fiddle file into tests • possible even add Rich comments in those tests

neumann21:11:25

I think @nate has a way of automatically re-running tests when related files get saved, but I haven't done that yet myself.

neumann21:11:17

I definitely agree that "fiddling" is for figuring out the code and testing is for pushing on the edge cases and making sure future changes don't mess stuff up!

neumann21:11:28

@nate and I also treat fiddle files as mini-workflows. We call those "topic" fiddles. We'll document parts of a process. We put textual comment in the file and comment blocks underneath it for each step. For example:

(def selected-date "2020-10-28")

; Events for the day
#_(->> @(http/request (game-data-query-for-date selected-date))
       response->events)

; Events for the week starting on the date
#_(->> @(http/request (game-data-query-for-week selected-date))
       response->events)

; Player info in all the events for the selected date
#_(->> @(http/request (game-data-query-for-date selected-date))
       response->events
       (map (juxt game-id event-type (comp :player event-data))))

neumann21:11:30

Another example of a "topic" or "workflow" fiddle:

(comment
  ; Re-create all the EDN tables from the tables in the "stats-by-hero.csv" file
  (util/reimport-tables fiddle.stat-table/stats-file)

  ; Browse each of the stat tables to make sure they imported OK, the labels
  ; look OK, the stats are the right type, and they point to the right hero.
  (fiddle.stat-table/check-table :stat-card-6)
  (fiddle.stat-table/check-table :caster-6)
  (fiddle.stat-table/check-table :live-4)

  ; Dump out the raw data for reference. (Not terribly useful.)
  (fiddle.stat-table/dump-table :stat-card-6)
  (fiddle.stat-table/dump-table :caster-6)
  (fiddle.stat-table/dump-table :live-4)
  )

jrwdunham21:11:37

Oops, I referenced Ian when I meant to ping @nate and @neumann. Anyway, thanks for those examples and elaboration.

jrwdunham23:11:25

@neumann I use spacemacs and cider and can run a namespace's tests or a single deftest with a keyboard shortcut, so I think I get equivalent functionality to what you're describing with vim and fireplace. I too end up following a workflow (though maybe I hadn't formalized it as carefully as that) similar to what you itemized, and I've definitely been writing those kind of "topic" comments in repl/fiddle namespaces of late...

jrwdunham23:11:33

In particular, though, I've noticed that "fiddle" code does not readily lend itself to direct translation into "test" code. I've ended up in situations where I end up writing a test assertion as:

jrwdunham23:11:39

(is (->> (copied-from-fiddle-file ctx)
           run-assertions
           (every? true?)))

jrwdunham23:11:26

which is not ideal in terms of the insight into what is going wrong in run-assertions

nate23:11:48

I agree, I don't often copy code from fiddles to tests

nate23:11:24

fiddling is more about exploration, testing is about locking functionality in

🔥 3
nate23:11:39

I spend time in fiddles where I figure out where something is going

nate23:11:57

and once I get there, I will decide whether I need to write tests about it

nate23:11:20

and writing tests is like setting up wooden forms and pouring concrete around the implementation

jrwdunham23:11:21

nice simile 🍷

nate23:11:09

I never really understood TDD before clojure, and fiddle files have really convinced me that it's untenable

jrwdunham23:11:20

yeah, so maybe the temptation to seamlessly merge fiddle code into test code should be resisted

jrwdunham23:11:36

interesting. So any generalizations on how you decide whether something needs tests? Do you write property-based tests regularly?

nate23:11:43

yeah, just because they both exercise the code in multiple ways doesn't mean they are identical and should share implementation

nate23:11:49

if it's a smaller transform or I/O, I usually don't write tests

nate23:11:10

they can cause an inertia in the code that is antithetical to progress

nate23:11:59

usually if it's something particularly core, like decoding a bespoke data format, or a pure engine (such as one that will combine and aggregate sports stats from multiple games into one set), I'll write tests to make sure that it will continue to work when we need to change it in the future

nate00:11:47

I haven't yet written any property-based tests. I would like to investigate using them, but haven't so far

jrwdunham00:11:43

property-based tests are interesting, but often I find that I they simply cause me to re-implement something in a different way and test that both implementations generate the same results given equally spec-conformant inputs

jrwdunham00:11:28

which can seem wasteful, but might be a good "laying-bare" of what testing actually is...

nate00:11:36

that is a very keen insight

nate00:11:24

much of the stuff I've recently tested would require a non-trivial generator to make the inputs as internally consistent as my hand-crafted data

jrwdunham00:11:03

yeah, and crafting that generator would probably be a long way towards reimplementing your core logic (i'm guessing)

nate00:11:11

precisely

neumann00:11:14

For me, tests are most useful for testing the edges of a pure data model.

neumann00:11:29

@nate and I end up with a lot of simulation style logic, so we have to handle different events that affect the state. It’s all a pure reducing-style data model.

neumann00:11:54

The functions take the state and an event and return the new state.

neumann00:11:34

Like what we talked about in our first couple of episodes on Tic Tac Toe.

neumann00:11:07

Tests are great to ensure certain properties are preserved by the state transforms.

jrwdunham00:11:02

Yeah, that all makes sense to me, I think. It can be a fine line not crossing those edges and ending up testing the database/API on the other side of that line though.

jrwdunham00:11:39

but I think you're right that the edges are the place to look for where testing might be valuable, or particularly hairy blobs of logic (pure functions)

jrwdunham00:11:30

i've been working with someone who's been telling me that we write tests when we can't understand something, so we should probably just understand it better

nate00:11:41

Agreed. Tests are a tool to make sure code keeps on working, not to understand it. Fiddle code is a great tool for understanding code.

ianjones19:11:30

Thanks for the replies! this was a super insightful conversation. I love how exploratory REPL driven development is.

ianjones19:11:27

I write a lot of Ruby for my day job and spend quite a bit of time in the ruby console building out what I want. Makes me want to ditch ruby and just use clojure 😛

neumann22:11:51

@ianjones I can say that I've written a lot of Ruby, Java, Groovy, Scala, and Javascript, and now that I've been on Clojure for the last 4 years and ClojureScript for the last 1.5, I haven't really looked back!

ianjones22:11:58

have you recorded an episode on how you made that transition?

neumann21:11:54

Yes! That's it!

neumann21:11:57

@jrwdunham I definitely have parts of a fiddle file that I copy and paste over into a test file. I even have "Rich" comments in test files too. That's kind of a hybrid. I use vim-fireplace which detects test files and re-runs the tests automatically when I reload the namespace for that file. It's a fluid process, but in general, I: • build a "Rich" comment in some source I'm working on • move that comment over to a fiddle file as it grows • move parts of that fiddle file into tests • possible even add Rich comments in those tests

neumann21:11:28

@nate and I also treat fiddle files as mini-workflows. We call those "topic" fiddles. We'll document parts of a process. We put textual comment in the file and comment blocks underneath it for each step. For example:

(def selected-date "2020-10-28")

; Events for the day
#_(->> @(http/request (game-data-query-for-date selected-date))
       response->events)

; Events for the week starting on the date
#_(->> @(http/request (game-data-query-for-week selected-date))
       response->events)

; Player info in all the events for the selected date
#_(->> @(http/request (game-data-query-for-date selected-date))
       response->events
       (map (juxt game-id event-type (comp :player event-data))))