8 ms·
I have a concern about REPL driven development, which is around how maintainable the result is. The reason people like REPLs is because they can experiment to
by zmmmmm 6y ago
I have a concern about REPL driven development, which is around how maintainable the result is.
The reason people like REPLs is because they can experiment to discover the right way to write the code in a very fast feedback loop. This alleviates a huge pain when you are operating with uncertainties or at the edge of your knowledge - 'does this function return null if no matching entries or an empty list?' - etc.
The problem is that the reader of your code is not going on that journey with you. They are coming in naively without knowing all of that experimentation you did. The writer now has the opportunity to create code that "just happens" to work by a magic of coincidences and conveniences based on very subtle non-obvious properties of the APIs and systems they are working with. The reader is severely disadvantaged and will have a much harder time to reach parity with the state of knowledge that the writer had. For example, some property of a function or object may not be documented at all, but through the REPL the writer has ascertained it is true. How can the reader know this?
So its a double edged sword in that the more you empower the original creator of the code, the greater imbalance you create to the later readers and maintainers of the code. Used well and with great discipline I could see it being very powerful and positive force to make better code. But in the worst case scenario, you'll end up with extremely unmaintainable code.
- avmich 6y ago> the more you empower the original creator of the code, the greater imbalance you create to the later readers REPL gives you more tools to shoot yourself in the foot, but you're not required to use those. Most programmers can write spaghetti code in many Turing-complete languages - not that they do that all the time. In other words, REPL gives you powerful tools - which, yes, can be misused. They are still powerful, and can bring success when used well.
- marcosdumay 6y agoMore than Lisp, SQL is a REPL-only language. The complexity you can achieve there without some interaction is very small. After some on the field experience, my conclusion is that this is not a large problem. Yes, you were not there on the initial design and does not have all of that acquired knowledge, but you can always run your own tests and formulate and verify your hypothesis. The REPL is there for debugging too, not only for the original design.
- tester756 6y ago>SQL is a REPL-only language. The complexity you can achieve there without some interaction is very small. ha ha. unless it's giant sql with 5 sub queries and poorly formated for bonus points good luck
- marcosdumay 6y agoHum... 5 sub queries? Nope, I'm talking about bigger stuff.
- TheAnswerMan 6y agoSo REPL may make people less likely to document the code? I don't see the correlation.
- discordance 6y agoCould this issue of expressing the journey be addressed by leaving code/markdown breadcrumbs in Juptyer notebook like cells?
- dragonwriter 6y ago> For example, some property of a function or object may not be documented at all, but through the REPL the writer has ascertained it is true. How can the reader know this? Well, the writer could document their code. Or, the reader could discover behavior of undocumented code the same way the writer did; presumably the “reader” will also have a REPL available.
- koz_ 6y agoThe author of the code is going to have to discover how these APIs work somehow, right? Why would the code end up worse if the author has access to a particular tool for interacting with these APIs? A shell and curl is a REPL for interacting with and exploring HTTP APIs but I don't think anybody would claim that the use of such tools makes code written to those APIs harder to follow. In fact, one of the benefits of your REPL being integrated with one's editor, and being in the language of the system, is that the kinds of ad-hoc scripts you write when doing exploratory / debug coding which one would typically discard after use can often times simply be committed, either as utility functions or test cases. Far from pulling up the ladder on devs who come later, having a REPL makes it more likely for them to have access to the same tools used to build and understand the system in the first place.
- discardable_dan 6y agoI have worked on massive LISP projects that were developed using REPL-driven development, and your fears are critically valid. Even worse, this basically hamstrings it as a standalone application. The workflow to _use_ the application becomes "start the REPL under emacs and launch it", which is, in my personal opinion, a completely unacceptable distribution model. And if you don't use emacs, good luck to you! Once set up as REPL-based development, Smalltalk and LISP both have to essentially ship their "whole environment" to deploy the application. This only works out well for small projects that do not need to be distributed, or whose distribution you, the developer, have a lot of control over. (There's an aside to be said about building binaries from a LISP compiler, but most binaries I have seen produced by such compilers still need access to the required libraries, which sort of hoses it as a distribution solution. This could be a result of the build system used on the projects I work on.) And your documentation concern is valid, but I think even worse is the design concern: when designing an API, you have to stop and design it and consider the various concerns. If you develop it as you need it / as you go, it is possible to arrive at sub-optimal designs. Hacking the "next piece" out at each step is likely to lead to slanted designs with poor APIs, requiring future refactoring. You see this with programmers at every level: if you ask them to write a program to do a single thing, then add more (related) capabilities, and continue ad nauseum, at some point the program will need to be refactored. This suggests that, without prior planning, REPL-based design is set up to incur technical debt. Unfortunately, my real-life experience confirms this. Moreover, refactoring something in a REPL is... a chore, to say the least.
- mikelevins 6y agoYour observations are valid, and need to be kept in mind when developing in an exploratory, interactive way. Your alarm seems a little overblown. Maybe it reflects a particular bad experience? I've delivered a bunch of products built in the way I prefer to work, and it's been a long time since I've had the problems you're describing. Maybe that's because I ran into such such problems early in my career and learned ways of handling them. And yeah, if you just iterate and explore, you can wind up in lacunae. You do need to develop some actual goal and a vision of how to get there. Interactive development can be really helpful for the exploring part, but it can't pick your goals for you. In short, interactive development is a way of working that's good for some cases and some people (me, for instance). It's not a cure-all or an objectively superior methodology for all people or all cases. I don't want it to take over the world. I just want it to continue to be an option because it's the way I prefer to work.
- thewebcount 6y ago> But in the worst case scenario, you'll end up with extremely unmaintainable code. Actually, isn't it far worse than that? Say you're stopped at a function call, you call it, and it does the wrong thing. You edit a variable that's passed as an argument to the function and call it again. Now it does the right thing. Great! You fixed the bug, right? Except that you don't actually know that your program can actually ever end up in that state naturally. Where did that variable originally come from? Was it entered by the user? Was it read from a file? Was it from a table? If so, does the table contain the modified value? This seems like a recipe for problems to me. I'm sure it's fine in simple cases where you're just changing the value of a constant or something, but it seems really likely to lead to incorrect reasoning about the code while you're working with it in any but the simplest of cases. Am I blowing this out of proportion?
- mikelevins 6y agoWell, no, you didn't fix the bug. You collected a datum about the bug: it caused a variable to have a value that it should not have had. Now you know something about it that you maybe didn't know before. When you say "Great! You fixed the bug, right?" it sounds like a paraphrase of a passage in my essay that I had some misgivings about when I wrote it. But I thought, surely nobody's going to think I really mean the problem is definitely solved? Surely, people will realize its a bit of hyperbole. Maybe not. Maybe I should have instead written it more soberly. Maybe I should have just said "Now you've collected a bit of data about the nature of the bug."
- tareqak 6y agoIf there was a way to “playback” the REPL and store it for later, then it would be possible for the subsequent users (or even the same user) to see how the person using the REPL got to that point. It would not be a replacement for documentation, but it could be in addition to documentation or even a starting point.
- Buttons840 6y agoCode developed in a REPL would ideally be "pure", meaning it takes a value and returns a value without side-effects. Then others can experiment with it to their hearts content. Maybe it's ok to read from a database, but don't write. If you write to the database nobody else will dare touch your code.
- mikelevins 6y agoThat rule absolutely does not work for me. For me, the whole point of the repl is that I want to build something incrementally, interactively, building it a little at a time by making small changes to it. I want it in memory, running and responding to my changes as I make them. Put this here; put that there. Change that around. Let me look at it; nope. Put that back where it was. Now add this. If the repl is stateless then the whole purpose is defeated.
- Buttons840 6y agoI don't mean the REPL is stateless, but the code developed using the REPL would ideally be stateless. Stateless code would be easier for others to experiment with in their own REPL, without "oh no, you broke production with your REPL!"
- Jtsummers 6y agoYou don't connect (necessarily) the REPL to production. The code can be stateful code if you're not careless. Just like you oughtn't log into the production database server and write/update/delete data entries directly. This is about the development phase more than the production phase. Do something like this (directly in the REPL or in a buffer as mikelevin has described doing elsewhere): (defparamater *db* (connect-to-test-db)) (query-db *db* (make-query some-query-description)) ;; see that it pulls out the desired record (let ((record (query-db *db* (make-query sqd))) (update-record record) (write-to-db *db* record)) ;; update the entry ;; rerun the query above to see that things changed as expected ;; rewrite that let as a defun: (defun make-update-to-some-record (sqd db) (let ((record ...)) ...)) ;; test that it works (make-update-to-some-record sqd *db*) ;; see that it does in fact do the same as the let above. ;; move that to a permanent source file, and build it ;; into the system. ;; move tests into test file. ;; repeat with next feature/task The functions run in the REPL are stateful, but we aren't careless about what they're touching. NB: You can connect to production environments. You can even connect to live, production lisp images. This could be useful for: profiling real-world activity, debugging real-world problems, applying hot-fixes. But especially that last one should be done carefully, and ought to have been validated using a test environment first.
- juststeve 6y ago100%, and that also applies to the original author. Building a script from the REPL might mean running different parts of the code out of order (such as reloading a function definition). As you mentioned, it's easy to lose track of state, writing 'clever'/unmaintainable code, and forgetting the order and purpose of the code.
- IlliOnato 6y agoI think your concern is valid, but I think it's the general case with any powerful tool: it can be abused, or shoot you in the foot, and requires discipline to apply. I don't have REPL-driven development experience, but I think we are all familiar with StackOverflow. This is a great, great resource, but it lead scores of programmers to the "style" of development, where they mindlessly copy solutions from SO, without understanding of how and why they work. I guess one can extend the old maxim "there is not now, nor ever will be a programming language that makes it easier to write a good program than a bad one"... (edited grammar)
- heresie-dabord 6y agoI agree with you that the immediate start-up and feedback is a great benefit to the coder. This is why I dislike complex, Rube-Goldbergian REPL systems. There is a use-case for a throw-away interaction with a REPL. For example, how does $builtinFuncX work, or how would $data best be imported into a structure? A REPL can also be a good initial approach to a more ambitious problem. In this case, a REPL can be good for focus and discipline. If the second case is going to answer your concern and be constructive, it's necessary to be able to build the code for sharing and cleanly export the code for re-use. I've had success tackling challenges using REPLs for Python and Perl [1] in both ways. But no tooling is going to solve the problem of a sloppy teammate who claims success just because "it compiles" and "it works on my box". A person who knows how to build good tooling goes further. [1] https://github.com/viviparous/preplish https://github.com/viviparous/preplish
- reddit_clone 6y agoWhen a certain thing is done in repl, it usually converts into a function or two in the source code. The ad-hoc testing code is converted into unit testing. That should help with maintenance no? One big theoretical advantage I see in repl driven development is, I can be sure that _all_ the code has been executed at one time atleast. (In a normal edit-compile-link-test language cycle, I can not be sure of that)
- xapata 6y agoYep, just copy-paste a bit of REPL history for a docstring, then use `doctest` to make sure it holds. Pretty soon you'll have ~100% test coverage. Minus the error paths you didn't test in REPL.
- Scarbutt 6y agoSure, but the point is that it requires more discipline, since the REPL can encourage you to be more lazy/sloppy.
- Jtsummers 6y agoOnly if you aren't delivering something. No one (sane) is delivering a lisp image as "The Product" without also having a way to regenerate that image. If you write something in the REPL and never convert it to a proper function/class/struct/package in source, you're hosed when your system reboots and that image is lost. This requires no more discipline than, say, actually committing files to a version control system to collaborate with others. Versus shouting from the hills, "It works on my machine!" and being confused when it turns out that you didn't commit "foo.c" and compiled it manually rather than updating your Makefile and committing both changes so others could use it.
- webmaven 6y ago> No one (sane) is delivering a lisp image as "The Product" without also having a way to regenerate that image. Oh man, you are so wrong about that. It's worth noting that this style of development also hamstrung quite a few Python web app projects in the late 90s because the Zope application server encouraged the use of this style of development (except through a browser rather than the command line) and beginners eventually got to a point that they were stuck and needed to make the leap to a completely different workflow of development based on files on the filesystem and in version control instead for their code, while content (ie. application state) for each deployment of their app remained in the embedded object database. A frequent lament was "if you knew I would eventually have to switch to filesystem based development, why didn't you have me start out that way?"
- wtetzner 6y agoI really don't follow. If you're programming in a language without a REPL, don't you have the exact same problem? If the author doesn't document their knowledge, then it's lost for readers. I don't really see how a REPL makes any difference there.
- majormajor 6y ago> The problem is that the reader of your code is not going on that journey with you. They are coming in naively without knowing all of that experimentation you did. The writer now has the opportunity to create code that "just happens" to work by a magic of coincidences and conveniences based on very subtle non-obvious properties of the APIs and systems they are working with. The reader is severely disadvantaged and will have a much harder time to reach parity with the state of knowledge that the writer had. For example, some property of a function or object may not be documented at all, but through the REPL the writer has ascertained it is true. How can the reader know this? If these sorts of things aren't documented then not having used a REPL won't save you. Someone ran a function with a println or in a debugger, figured out the answer, then removed the debug stuff... but did it by repeatedly compiling/running instead of in a REPL. (Or, my personal favorite... someone didn't bother to run that particular code, but just made some assumptions, so has no idea those edge cases are even there...)