Hacker News new | ask | show | jobs
by gered 1347 days ago
This is my experience exactly. I love the language and ecosystem in a lot of ways. I also believe that REPL-driven-development is a ridiculously productive way to work.

But I absolutely hate maintaining an old Clojure codebase (unless it's tiny).

The REPL helps a lot with discovering what the proper way is to call any random function you have in your code, but this is still really super annoying. I really hate to get into a dynamic-versus-static-typing debate, but I've long since come to the conclusion that -- all other things being equal (hah!) -- if I have to dig into a large and old project, I'd much rather have types by my side than not. Code will not ever be adequately documented or commented (and even if it _seems_ to be at first glance, you will always have nagging doubts about how up to date that info really is). This is where type definitions help to figure out the shape of the data that any piece of code is working with. People talk about adding spec/schema definitions but that doesn't solve all the problems with not having type definitions unless you add these spec/schema definitions _everywhere_ ... and let's face it, you just aren't going to do that in any Clojure project. So, best case scenario is you still have a large collection of functions in your project that are calling each other, etc that you are left having to deduce yourself what this random map or list actually contains.

2 comments

As a Clojure admirer who has learned it enough to get things done (and subsequently forgotten it) twice, I recently had the positive REPL experience that people talk about. Or at least I think I did. I was able to write a web scraping tool almost entirely in the REPL.

However, I find myself running Ruby/Rails (console) in debug mode in RubyMine (Jetbrains IDE). The REPL-like experience seems quite close to that of Clojure, with the added benefit of being able to easily enable and disable breakpoints and see my local data (and snapshots of all previous local data up the call stack).

And honestly, the Clojure thing that always stops me from actually getting a full anything built is the lack of frameworks and approaches which have critical mass. It's always the same answer, "We like to choose our own libraries." That's lovely once you know what you're doing, but the ramp-up time is just SO much slower than with other languages.

Considering the time to working proof of concept is critical quite often, few beginners have time to figure out all the libraries and tooling and integrations to build something in Clojure.

I think despite still wishing I had a Clojure backend with Clojurescript/React frontend, I'll step from my Rails PoC to a Phoenix/Elixir product and be successful and happy (and have a lot of people doing something similar, with similar tools and libraries).

Yeah the (what I personally call it) "choose your own adventure" style approach to Clojure projects, where you don't use a framework like Rails, but just string together your own project from separate libraries, is really both a "pro" AND a "con".

It's great when you know what you're doing, and indeed, I have my own personal Leiningen templates to set up a Clojure project the way I like it and to save myself some time. Bigger project templates, like Luminus, I often find personally aggravating because I often feel like it just barfs a whole bunch of unnecessary and semi-complicated (in my opinion anyway) code in a new project even with the most minimal options chosen. But that's the power of the ecosystem ... you can create your own project templates to meet your own needs.

But a new developer getting their feet wet in this ecosystem? Yeah, it is hard. And even if they use an existing project template like Luminus to bootstrap their project ... well, the project template only helps generate the initial project. Ongoing maintenance for updating dependency versions and keeping a working integration of the libraries it initially set up for you (with respect to newer versions and any API/config changes, etc) ... well, those responsibilities are all on you! Kit (another newer successor to Luminus) _may_ provide some better alternatives here, but it'll still be limited with exactly how much it can help here. But I think it's still much too early to say one way or the other with Kit, so who knows.

(Also thanks for sharing your Ruby/Rails perspective on REPLs. A colleague of mine made some similar comments to me when we were discussing REPLs a while back, and I've not spent any time with Ruby so couldn't comment. It's interesting to hear! Most other REPLs I've used outside Clojure were not too useful as anything other than quick toys for trying short snippets outside of the context of a full project.)

Don't know about Ruby but agree that languages(think Java/C#/Javascript/C++) with really good debuggers don't have anything to envy from the Clojure REPL. But to be fair, companies have invested who knows how many millions to get that level of tooling while in Clojure and the nature of lisps you get it for free and low effort.
i'm starting to feel that REPL driven development can be bad for maintenance, when you have a REPL, you can write ridiculously compact and abstract code that is hard to understand just by reading it.
That's an interesting point I'd not thought of. I guess I'm more looking at it through the lens of "interacting with and modifying a running system" which kinda gives you a debugger (ish), compiler and execution environment all rolled into one. It's kind of nice to work in this kind of "scratch pad"-like environment while you figure something out versus the more traditional edit-compile-run cycle. But I have definitely seen what you describe as well, so I think that aspect is worth considering too, for sure.
It's almost by definition in some sense ... if it was obvious how to write the code you wouldn't need the REPL so by definition if it is uesful it must be producing non-obvious code.

That is a slightly shallow take though because its not really symmetrical like that. You can use a REPL as a way to arrive at the most easily understood rendition of something. But that is very prone to subjective bias as lots of things seem obvious in retrospect that are not at the start.

To the extent that the answer you eventually arrived at is a result of learning a mental model of the data and functions surround it, it will leave a significant residual gap for the next person who comes a long to bridge.

With great power comes great responsibility.

When using the REPL you can quickly experiment until it does what you want, but you need the discipline to distill something maintainable from it afterwards.

I agree. More generally, REPL-driven development rewards "guess-and-check" programming, and discourages "think-then-act" programming. This isn't a formula for success over time.
> ridiculously compact and abstract code that is hard to understand just by reading it.

For a maintenance provrammer against learning anything, abstract and compact are always bad.

Typically though, if you understand a few core concepts abstract and compact can be more readable towards the end goal of a high level understanding.