Share the nitty-gritty details of your Clojure workflow!

One of the central selling points of Clojure is a superior workflow, based on ancient lessons of Lisp. :clojure: But talking to Clojure devs, I find wide variation in what that means. There are real differences in Clojure/ClojureScript workflow from one person the the next.

I think we can learn a lot by comparing notes on those tiny workflow techniques. Not just the broad strokes of “I use CIDER” or “I use ‘lein repl’”, but the specifics like “I always define a function name before I start writing the code” or “I work in the command-line REPL until confident in the expression, then copy it over to a source file”.

So, let’s each of us share the excruciatingly minute details of our Clojure or ClojureScript process. :smile: Here are some brainstorming questions, but please feel free to describe your workflow freeform.

:bmo: What kind of REPL do you use—command-line, CIDER’s REPL buffer, or integrated with your project files? If a file, which ones?

:sunrise_over_mountains: How do you deal with state in the REPL, such as let bindings or other local vars?

:paperclip: Do you save your REPL explorations—other than any “official” result like a def'd function—off to a file? Where? Does that file go into source control?

:books: Do you write tests directly from what you write at your REPL, or separately? From where you run those tests—the shell, CIDER, only on a CI server?

Finally, let’s all remember that a person’s workflow is close to their heart. In these kinds of discussions it can be easy to accidentally insult someone’s method of doing things. We can each minimize this by doing our best to describe what we ourselves find important in our own workflow. When it comes to other people’s methods, try to see what they find valuable, even if it’s very different.

I’ll come back in a while to report on my own process. I look forward to hearing how you get things made in Clojure-land! :smiley_cat:

30 Likes
:bmo: General REPL use

I mostly use inf-clojure (an Emacs mode). Most of the time directly evaluating expressions inside my projects files. Sometimes using Emacs’ scratch buffer if there is no project or it seems better for other reasons (those reasons are rare). In general I type very little inside the REPL itself and everytime I do so I get slightly annoyed by not being inside a proper text editor.

:sunrise_over_mountains: Recreating an expression’s environment

It depends a bit if I want to use something many times or just once. In a component based project I might have a def for individual parts of the system so I can just pass that to functions which expect it. That def is always inside a (comment) block at the end of the file so it’s not evaluated by default.

When testing smaller functions that work on simpler data I often just use a let binding and supply sample data manually. If the data gets a bit more complex I might use a helper function to generate the test-data.

:paperclip: Saving REPL explorations

As mentioned above most of my REPL work happens inside files, sending expressions to the REPL whenever I want to try them out. So in the end the result I’m after is already in the right place and I usually just commit it with git or similar.

:books: Writing and running tests on the REPL

Same thing basically, I write tests in a buffer and have a little (comment) block at the end of the file to run the tests in the current namespace. Sometimes I forget and don’t run tests, then usually a CI email is in my inbox telling me that I messed up :slight_smile:

7 Likes

I can group my work rougly into two categories: finding the right transformation to produce a desired data structure, and figuring out how to use a library that someone else has written.

For the latter case, I start by setting up an environment where I use the library. On a REPL prompt in a Terminal window I’ll try to call the function, often with incomplete arguments. I’ll look at what it returns. If it returns the wrong result or throws an exception, I see if I can fix that particular problem. A side benefit is that I know how the function fails if it does (silently? throw? incorrect results?).

I proceed step by step to get some idea of what parts the library is composed of. Sometimes fixing problems requires passing in more complete arguments. In such cases I’ll write a function that produces test data (make-customer or the like). Sometimes I’ll store that data in a var. But I tend to use as little state as possible, as state makes it easier to reproduce results.

Hopefully I’ll eventually arrive at something that works. The next step is to start from a clean slate and to rerun the experiment. This step is easy in figwheel (reloading the browser window) and lumo (simply restart the repl). In Clojure restarting the REPL takes longer, which annoys me. More often than not, the series of forms I entered won’t work exactly as expected because it previously relied on lingering state so I need to re-define vars or change names.

Once I can reproduce the functionality reliably, I’ll copy the forms into an Emacs buffer visiting a Clojure(Script) namespace. Often this is a user.clj(s) - essentially a long file serving as an extension of my terminal REPL. I’ll stick the code in functions and try to give them sensible names. If I can’t come up with a good name, I’ll call the function “banana” and rename it later. Now I need to send the code to the REPL again. With Figwheel, that happens automatically on save; in Clojure I use cider-eval-buffer or its Unrepl.el equivalent. Code eventually graduates from user.clj(s) to a proper namespace.

The process for data transformation functions is similar, but I often start with a threading macro. Starting from (->> ["a.txt" "b.txt"]), I’ll add transformation steps. Because multiline editing is inconvenient, I usually try to keep things in one line. I use ^P (or Cursor Up) to recall the previous line to fix issues or extend the pipeline. At each additional step, I make sure I get immediate feedback.

Again, once things start looking good, I’ll clean up the code, copy it to Emacs, fix the formatting, introduce lets and commit a “WIP” checkpoint to git. For testing, I return to my separate Terminal window an rerun the function from there. This is convenient because I usually just press Cursor Up to recall the previous testing command. I can also easily compare the result to the output of previous commands.

As you can see, this workflow doesn’t make heavy use of Emacs features for REPL interaction (though I use paredit, smartparens etc. extensively). In a nutshell, I live in the Terminal REPL; I eschew Emacs’s various terminal emulator offerings in favor of iTerm2 on macOS and gnome-terminal on Linux.

I’m fond of this workflow, because it keeps the cognitive load manageable and does not depend on a complicated tooling setup. All I need is a Terminal prompt provided by unravel, lumo or figwheel. I do require a prompt with full Readline functionality (like rlwrap) as I rely heavily on ^R to search my readline history for words.

6 Likes

This step-by-step explanation is great!

:bmo: General REPL use
I use Cursive and its REPL with Figwheel for ClojureScript. For UI code Figwheel takes care of reloading components to re-render them in the browser. For data transformation code I usually type it in the editor, send forms to REPL and integrate the code with application code once I’m satisfied with the result.

:sunrise_over_mountains: Recreating an expression’s environment
Since in ClojureScript you usually keep entire state in a single place you just have to define state globally so you can always refer to the atom.

:paperclip: Saving REPL explorations
Since I experiment right the editor all the code is already here, maybe a bit of refactoring before committing it.

4 Likes

I use Cursive/IntelliJ, so all steps below happen inside that editor.

ClojureScript process - For the past couple of months I’ve been using shadow-cljs for my ClojureScript workflow.

  • I set up npm scripts so that no matter who is running the project, they can run npm run watch or npm run release to start a dev server/compile or build a production release, and not have to think about the particular project config (which may change). The only dependencies for a new programmer are node, npm, and JVM.
  • Using shadow-cljs means I consume javascript deps directly from npm and shadow-cljs packages them up with compatibility on the level of webpack. Previously I used figwheel and often had a separate webpack build for deps that are not Closure-compatible.

:bmo: REPL Config

I am one click away from a Clojure (JVM) REPL. I put a symlink from target/shadow-cljs/nrepl.port to .nrepl-port, and my Cursive repl config is set to “Use Leiningen REPL port”.

To convert a Clojure REPL into a ClojureScript REPL that is connected to a browser, I run a saved REPL Command called shadow-enter :browser which will connect me to the current :browser.

;; this is in a saved REPL command,
(in-ns 'shadow.cljs.devtools.api)
(repl :browser)

One minor nit, I always have to manually change the cursive repl from clj to cljs because it doesn’t pick up this change.

:bmo: REPL usage

I almost always send forms directly from the file I’m working in.

For local state during dev, I might def vars, or wrap a block in a let.

:paperclip: Saving REPL explorations

A lot of what I send to the REPL are expressions inside (comment ...) blocks, which are basically small tests that verify/demonstrate how a particular function behaves (or should behave). Often these are worth keeping around, sometimes to serve as additional documentation, and other times to migrate to dedicated test namespaces as the project stabilizes.

:globe_with_meridians: naming

I make heavy use of Cursive’s refactoring tools, especially renaming of symbols and keywords. Whenever I think I might want to rename a keyword in the future, I namespace it, often with my own chosen namespace rather than one based on the current namespace (eg. :query/loading? vs ::loading). Cursive’s ability to globally rename namespaced keywords (including in destructuring forms) is great.

4 Likes

In my current project (first real-world big CLJ project) I do both Clojure server-side and ClojureScript in the browser. It’s based on boot.

:bmo: General REPL use

Launch in 2 seperate terminals the front task and the back task (boot front-dev and boot back-dev). They do vastly different thing so I’ll try to explain for them.

CLJ : our boot back-dev task start a nREPL (both server and client), in which we start our project with a custom function (user/go). We use integrant (alternative to Stuart Sierra’s component). I often use this terminal REPL to test small expression, read the doc, and reset my code with Integrant REPL facilities. It allows me to kill/restart this process quite rarely. I connect my editor (Cursive) to this REPL to re-evalute expressions from my editor directly when I work in the back-end side of things, otherwise the Terminal REPL is enough.

CLJS : our boot front-dev task a nREPL server only and with boot-cljs-repl + boot-reload we’re able to recreate something pretty-close to Figwheel. I almost always connect Cursive to this server and interact with my page through it. In some rare cases I tried writing ClojureScript code in Javascript in the browser’s console (cljs.core.map(...) for example) but I quickly stopped inflicting my-self such pain :smile:

:sunrise_over_mountains: Recreating an expression’s environment

CLJ : println is my good friend. I also sometimes use scope-capture, very useful since the CLJ code is a web-server and almost everything is request-bound. So basically I inspect what’s going on and directly modify in source file. The REPL is for experiment, often with data transformation like @pesterhazy said just above.

CLJS : same as @roman01la, the state is kept in a single place, so I just inspect it (I use @roman01la’s citrus lib, so pretty sure our worklow here is super-similar). To give more details though, I use Cursive’s ability to run REPL commands to group a bunch of requires that allow me to inspect the state (and other utilities). I just realized that the CLJS official compiler allows to do the same natively and that boot-cljs-repl exposes this functionality. I’m the only one that uses the REPL in the team though :scream:, other are still doing println everywhere…

:paperclip: Saving REPL explorations

When I explore in the REPL, I do it directly from the REPL itself (whether Terminal for CLJ or in Cursive REPL client for CLJS). When finished I copy/adapt the code in my source files.

:books: Writing and running tests on the REPL

Say what ? :sweat_smile:

4 Likes

(I can’t really separate my workflow from my preferred style of
program construction, so this answer contains some of each.)

Exploratory

The beginning of most new projects for me is trying to establish an
understanding of the problem domain. So, for example, I might fetch
something from a remote JSON endpoint using clj-http and have a look
at what it returns. The process for this would be to type the fetching
code (which converts the result into Clojure data structures, include
keyword-izing it) into a working buffer in emacs and then execute it
with cider-pprint-eval-last-sexp. This will pop up a new temporary
buffer containing a pretty printed version of the result, which I will
then inspect visually.

I’ll almost certainly need to extract and/or transform some part(s) of
the returned structure to use for further processing, so I’ll move it
into a let form in my working buffer. If the data is too big to use
as a literal, I’ll just save it as an edn file and use
slurp/read-string to bring it into the let.

Once I’ve got my data bound, I write the code to interrogate it within
that scope using cider-eval-defun-at-point, which determines the
outermost sexp and evaluates that. This is bound to a single keypress
so I very quickly iterate on the code within the let until it does
the right thing. This sort work also sometimes involves using
cider-eval-last-sexp to evaluate subcomponents of a given form so I
can dissect and verify its pieces as I go without retyping anything.

Because there’s typically no state outside of the let, code that
works in that scope also works as a pure function, and once the code
in the let works, I hoist it out to a function and try calling it
using the same test data from within the scope of the let.

Bottom up

I’ll repeat this process for each small piece I need until I have a
set of working functions with which to compose higher level functions
that get more of the work done. The process of writing those higher
level functions mirrors that of writing the lower level ones: start in
a let with some stub data, get the functions right, hoist them out.

If at some stage my expectations are violated, meaning my mental model
of the data/computation is incorrect, I back up and re-check my
previous findings by re-executing my functions against the test data
until I figure out where I went wrong. Debugging should always be
about achieving a more complete understanding, not just getting tests
to pass.

Useful residue

All the stub data used in this process is retained in the working
buffer, usually in one or more comment blocks. Once I’m fairly certain
that the functions are in good enough order to deserve tests (that is,
they work and are unlikely to change anytime soon), I move these
captured bits of data into tests.

There’s often a set of functions and/or blocks of code that continue
to be useful for additional exploration but have not yet been factored
into the main program in my working buffer(s). If it’s a small
quantity that’s relevant only to one namespace, I usually put in in a
comment block at the bottom of that namespace. OTOH, if it’s the sort
of project where there’s a significant quantity of code one expects to
run manually but that isn’t really part of the program per se, I’ll
move that code into a file with a name like repl.clj and comment it
heavily.

Notes

  1. the time it takes to test each code change should be as close as
    possible to zero, thus I resist anything that involves a context
    change (changing windows/buffers, copy/pasting more than absolutely
    needed, etc) – flow states are sacred
  2. hypothesis testing is a big part of this process, and learning
    about the data and its shape is central to the act of writing the
    program
  3. the parts of the program that accumulate in the buffer work, none
    of the code is written in anticipation of need, but rather in
    response to it, and at any stage I can execute what I have so far
  4. the keybindings I use to perform these operations are the same for
    every dynamic language I use, so my process is similar from Clojure
    to Racket to OCaml to Ruby to Python (an advantage of emacs
    relative to single language IDEs for polyglots)
9 Likes

The :clojure: flow

I jack into CIDER from permanent project files and use it to send
expressions to the REPL to be evaluated, with the results appearing
transiently right where I’m working. I almost never touch an actual
REPL. (This means I rely on the namespaces and aliases within each
namespace buffer, rather than explicit requires in the REPL.) I do
this for both new functionality and debugging. This is by far the most
central aspect of my workflow: direct interaction with existing
project code, and evaluating progressive versions of new code directly
in the buffer where its final form will live.

When creating new functionality I start by sketching out the shape of
data I have and the shape of data I want to have. Usually this means a
concrete example (of both input and output) that I type out in data
literals or copy from some pre-existing source; occasionally it’s
enough to describe it with a comment. Often this dummy data takes the
places of what will become a parameter name. I start writing and
evaluating expressions on that dummy input, moving back and forth from
evaluating the top-level form and evaluating its sub-expressions.

I may spit REPL output as text directly into the buffer I’m working
with, so I can munge it or evaluate pieces of it. When working with
large results I make use of first, take, rand-nth, and other
inspection functions, as well as intermediate defs. If necessary, I
create an expression’s “state” by defining dummy vars like foo or
xyz in temporary defs or lets that get removed when cleaning up
a function for its final defnition.

As my code gets close to its final form, I save the examples I’m using
off somewhere else to become tests. This transition is one area where
my process is still loose. I also sometimes save nonce code or
potentially-reusable snippets to a project-specific repl.clj
namespace that I keep separate from source control and compilation.

After writing the function and its tests, it’s time for lein run and
then lein test for a double-check. I run those from the shell but
would like to experiment with running them from CIDER. In general I’m
trying to reduce the number of times that I run tests manually,
because they’re slow.

Important tools :hammer_and_wrench:

Functionality I rely on in my Clojure environment, in approximate order:

  1. direct evaluation of expressions in permanent files
  2. Clojure indentation and syntax highlighting
  3. semantically-aware text selection (expand-region.el) and navigation (paredit)
  4. jump-to-definition (cider-find-var (M-.) and returning with cider-pop-back (M-,))
  5. docstring hints (via eldoc.el plus CIDER)

I consider each one of those to be fundamental components for me to
achieve a Clojure-based flow state.

For a long time, docs and examples have been the one aspect of my
workflow that was strangely not on this list. Instead of getting
Clojure docs with emacs, usually I’d have a browser open to
clojuredocs.org. I’ve just recently started using cider-grimoire
(C-c C-d r) for this purpose. I’d like to modify this part of my
workflow so that it looks things up locally instead of making an HTTP
request. (Down with 2-second delays!)

I look forward to hearing more folks share their development process :slight_smile:

5 Likes

Cursive connected to a remote lein repl process (nREPL, no socket REPL support yet) since I want that process to run longer than Cursive.

My CLJ workflow involves deftest and Cursive Run <test> in REPL. I have that plus Re-Run last test action in REPL bound to keys so I can easily “select” a test and run it on demand wherever I am in the source. No need to fiddle with the REPL window at all.

These are not tests in the traditional sense though. They often don’t even contain any assertions and may just print their results (like the REPL would). The “test” trick is just for convenience so that I don’t have to jump around my editor so much. Just hit the keybinding for “re-run” when making a change is nice. Depending on how much state the “test” needs I either re-create everything from scratch when executing or use a defonce atom somewhere.

I don’t usually maintain these “tests” either. They are isolated to their own source path and are for exploration only. Just a bunch of (deftest thing-im-thinking-about ...) in one large file or spread across many depending on the general “topic”. After a while these things typically break or don’t even make sense anymore. Sometimes they get deleted, sometimes they just stay as a reminder. Either is fine.

Basically these “tests” just serve as re-usable self-contained snippets that I can modify and invoke easily but don’t have to worry about “maintaining”. Sometimes actual tests are made out of these later though, which can also be used by CI. Just need to add the assertions basically.

I very rarely type into the REPL itself. I sometimes have (comment ...) blocks which contain a few instructions which I “Send X to REPL” but these would also be in the “test files”. I do have some other key-bindings which just send common things to the REPL like (repl/stop) (repl/start) or the combined (repl/go) which would start my dev setup. I could type these I guess but I’m lazy.

I would use this workflow for CLJS as is but unfortunately Cursive doesn’t let me (yet). Kinda got used to just abusing live-reload so I barely ever use the REPL at all for CLJS.

You can look at examples for this workflow in the shadow-cljs repo (everything in src/repl are these exploratory tests) and the repl util namespace.

7 Likes

That’s a really interesting technique, I usually use comment blocks containing the sample code which you’d put into your tests. Then I’m jumping between the code affecting the sample code and the sample code itself to evaluate it. A hot key to evaluate my “test snippet” sounds really nice :slight_smile:

3 Likes

with the results appearing transiently right where I’m working

interested in seeing a screenshot of this. In Cursive I always have a separate vertical column with the REPL history.

interested in seeing a screenshot of [results appearing transiently right where [Dave is] working]. In Cursive I always have a separate vertical column with the REPL history.

Hold on a second while I disable f.lux, because it’s 3pm in Berlin…

My default method is to evaluate in place (cider-eval-last-sexp with C-x C-e, for those of you following along in emacs), which makes the result display in two places: the minibuffer (at the bottom) and in a overlay that disappears momentarily (that’s the one immediately after the with-open):


I like this command because I can evaluate inner expressions as well as the top-level form.

My “sometimes” method is to use a prefix argument (C-u C-x C-e) so that the result is spit as text directly into the buffer I’m in, so I can work with it.


Talking to folks about these nitty-gritty details reminds me to play more with similar commands like cider-eval-defun-at-point (to always evaluate the top-level form regardless of cursor position within a subexpression) and cider-pprint-eval-last-sexp.

3 Likes

I have two main projects right now. One is an CLJS app for work, the other is an open-source library that I primary develop using CLJ (it supports CLJS, but most of my development is in Clojure). My answers are slightly different for each.

:bmo: What kind of REPL do you use—command-line, CIDER’s REPL buffer, or integrated with your project files? If a file, which ones?

CLJS-app: I start lein repl in the terminal. I then connect with cider-connect and then run

(use 'figwheel-sidecar.repl-api)
(start-figwheel!)
(cljs-repl)

My workflow might be out-of-date here, but I found that more CIDER features work when I do this - namely code navigation.

CLJ-lib: I start lein repl and use cider-connect.

In both projects, I then will create a (comment) block and start evaluating forms directly from my editor with cider-eval-last-sexp (bound to a keybinding, of course). The results show up inline in my editor.

Occasionally the results are too big too understand. I’m not too good at navigating the cider-inspect-last-result buffer, so instead I run cider-eval-last-sexp-and-replace to put the data right in my comment block. I can then use it directly in subsequent experiments, or name it with let or def.

From there, I tend to build up my expression with lots of tiny experiments, and then extract it to a function.

If I’m investigating a bug with an existing function, I tend to use some combo of:

  1. Adding println (usually prefixed with my initials like [:bhb {:x x :y y}] so I can grep for “bhb” to remove all debugging lines)
  2. Creating an example of the function call, then slowly comment out parts of the function implementation so I can see each step returned.
  3. Sometimes I’ll use “def” to define vars that mirror the local bindings, and then directly evaluate the inner sexps. I usually do this manually, but I have been trying to use scope-capture more to automate this.

I tend to run git commit -am "cp" (“cp” == “checkpoint”) often, so I can commit experiments, messy code, printlns, etc. When I have a logical piece of work to commit, I’ll clean it up, git reset <sha> to the last “real” commit, and then make a single commit of the total changes.

I use both Emacs+CIDER and Cursive: I write/edit nearly all my code in Emacs+CIDER, but occasionally open Cursive to look at warnings and also for renaming symbols and keywords.

:sunrise_over_mountains: How do you deal with state in the REPL, such as let bindings or other local vars?

This hasn’t historically been much of a problem for me. In CLJS, I can just refresh the browser if I notice a problem, which is rare. And in CLJ (perhaps because my project is a library without much state), I rarely run into bugs with stale state. In the rare cases where this occurs, my tests usually catch the issue quickly (see below)

:paperclip: Do you save your REPL explorations—other than any “official” result like a def’d function—off to a file? Where? Does that file go into source control?

I don’t normally save the REPL explorations. For experimentation outside of any specific project, I’ve been toying with a single project with a single namespace that is just a long log of REPL experiments. That project contains all the various tools I might use and I commit all my experiments, but it’s not part of my normal flow for either of my daily projects.

:books: Do you write tests directly from what you write at your REPL, or separately? From where you run those tests—the shell, CIDER, only on a CI server?

I do convert some of my REPL experiments to tests.

CLJS-app: I run tests on the terminal with karma. They re-run every time I save a file

CLJ-app: I run tests with lein test-refresh. They re-run whenever I save a file.

Although this feedback cycles isn’t as fast as I’d like (especially the CLJS tests, the CLJ tests are pretty snappy), I do like that this protects me somewhat from the occasional bugs resulting from stale state in my REPL.

I also run the full suite in CI, just as a final check (this often catches things like forgetting to commit a file :grimacing:)

1 Like

I forgot to mention it, but I am also a heavy user of the eval-and-insert-at-point approach.

This thread is full of learnings. Thanks to every one who contributed so far

5 Likes

I didn’t mention this, but I also use paredit. I have found that many beginners don’t realize that they will be much, much more productive once they learn paredit or parinfer. I worked in Clojure for a few years in my spare time without structural editing. I knew paredit existed, but I didn’t have a good sense of the time investment necessary vs the productivity difference.

It was only when I pair programmed with some experienced Clojurists did I realize how much time I was wasting on bugs related to parenthesis that they never experienced.

Speaking of tooling, I have been pleasantly surprised with how useful Joker is when integrated with my editor (Emacs in my case, but it can be set up with Sublime, Atom, and vim). Many typos that used to take me tens of seconds or more to identify and fix can now be fixed within a few seconds. I highly recommend it if you haven’t tried it.

6 Likes

I blogged about REPL-based development, eg using the REPL window vs doing things in an editor window, using autoloaders vs not, reloading everything from scratch without restarting the JVM, and how REPL-based ties in with other things (eg TDD). Hopefully it’s interesting to some people: http://blogish.nomistech.com/repl-based-development/

2 Likes

:bmo: What kind of REPL do you use
ProtoREPL in Atom. I start the REPL and keep it open for days. We use Boot so we can easily load new dependencies as needed in a running REPL. I typically have the REPL pane minimized to just three lines at the bottom of the editor and rely mostly on ProtoREPL’s inline result display (it lets you click to expand and drill into large data structures). We also use Component so we can easily start & stop processes and applications in the REPL as needed – and continue to evaluate new code into the live, running application while we work.

:sunrise_over_mountains: How do you deal with state in the REPL, such as let bindings or other local vars?
I either evaluate an s-exp (C-M-, b) or a top-level form (C-M-, B) or, occasionally, a whole file (C-M-, F). I write comments in the source files containing expressions I want to evaluate in the REPL, including defs for symbols that act as context for expressions in let bindings and function arguments, so I can C-M-, b those as needed prior to evaluating fragments of functions.

:paperclip: Do you save your REPL explorations
I’ve gotten into the habit now of using comment forms in source files for everything I would evaluate in the REPL so that my explorations are always available in amongst the source code (or the test code). Stu Halloway’s mid-2017 Chicago Clojure talk – REPL-Driven Development – talks about this: never type into the REPL itself: always type into a source file and just evaluate forms into the REPL.

:books: Do you write tests directly from what you write at your REPL, or separately?
See above. I don’t type code into the REPL at all now. I write comment forms in my source or test files and evaluate s-exp’s inside those comments into the REPL. I use Expectations for testing, with the new expectations.clojure.test namespace so standard C-M-, t (run test under cursor) and C-M-, x (run all tests in current namespace) hotkeys work (as if you were using clojure.test).

Happy to answer any questions that this approaches raises in people’s minds!

4 Likes

I have nothing particular interesting to add regarding my workflow – besides that I am using Atom with ProtoREPL. But I’ve just been watching Tony Kay’s Fulcro 2.0 Series which includes one video about his workflow which I quite like!

3 Likes