15 ms·
REPL Driven Design
- xapata 6y agoThat's the similar to the issue I have with coding in (Jupyter) Notebooks. It feels productive, but it discourages modularity.
- lvh 6y agoThe REPL in (Lisp) driven development is generally attached to your editor, not like irb or ipython or whatever: so the thing you're building is either the resulting function, or something that mostly looks like a test already anyway and is easy to copy into a test module.
- zelphirkalt 6y agoNot sure it really discourages modular code. I usually write code modularly in Notebooks as well, but not sure how this is for most people.
- Jtsummers 6y agoI suspect the problem is less that it discourages than that it does not encourage. Which gets back to the posted article. REPL-driven development permitted him to feel confident without doing his usual process, it didn't discourage decoupling, but it didn't encourage decoupling either. TDD, to be done well, almost requires decoupling your code to make it more testable. The same may (I'm not sure I agree, but I've never used Jupyter notebooks) be true of using notebook-styled development approaches and modularity. If the workflow permits relatively easy (and mostly safe) development without encouraging modularity, then it's incumbent on the developer to bring that discipline into the workflow.
- xapata 6y agoAnd unfortunately, most of the new crop of programmers don't have that discipline. I tell them to write tests and they eyeroll when they think I'm not looking.
- tchaffee 6y agoJeremy Howard developed the fastai library using Jupyter Notebooks and his nbdev tool[1]. I'm not saying that to disagree with your claim - I don't have enough experience with Jupyter Notebooks yet to have much of a strong opinion on whether or not it discourages modularity. Just that folks who want to have the best of both worlds might be interested in how it can be done. My first thought is that it looks like Donald Knuth's literate programming ideas put to practice, and it has me excited about programming again. [1] https://github.com/fastai/nbdev https://github.com/fastai/nbdev
- kmclean 6y agoJust to be fair, I don't think many practiononers of REPL-driven design would consider it a replacement for tests. It's an alternative workflow to TDD, yes, but the idea is that you still write comprehensive tests, they're just usually at a higher more system-y level than the ones you get from TDD.
- ch4s3 6y agoYeah I agree. It almost feels like he's making the argument in bad faith, but maybe he's actually unaware of the way in which you can back-fill tests?
- goostavos 6y agoIt does come off very strangely dichotomic. As though REPL driven means no tests and bad practices, whereas TDD means tests and good practices? As an idea, maybe we could do... both? Explore at the REPL, and then commit to tests and clean up once you've solidified your approach (i.e. how most Clojurists likely operate).
- sanderjd 6y agoI think TDD purists seem to see no point in unit tests that are not written before the code. That is, if the unit tests were not used to drive the design and implementation of the code under test, they are useless. I find a lot of value in the test driven approach, but this purist view does not make sense to me. I sometimes, or often, write the code first (being able to do so in a repl in languages that support that is even better), when I find it easier to think through the problem in terms of implementation rather than expected output. Sometimes I will then comment out the code and begin writing tests and re-writing the code to pass them. I think that's what he should have done here; he could have benefited from playing around in the repl, but reduced his fear of refactoring later on by having the tests.
- kmclean 6y agoYeah this is mostly how I operate when writing clojure.. development mostlty happens at the REPL and the bits that work get copied into a file somewhere and then once I have an idea how the whole system is going to work, I write an automated test for it instead of testing it manually. Tests replace manual testing in my workflow.
- ch4s3 6y agoHis argument feels like a straw man. You can and should fill in a few tests after you confirm something in the REPL. Maybe Clojure folks don't do that, but Ruby and Elixir devs often do. I would imagine he's familiar with that approach.
- mping 6y agoClojure folks do that. Don't know about all of them, but lots of them do. You even have emacs cider shortcuts to run tests from a live repl.
- capableweb 6y agoI concur. Use REPL driven development to arrive at a solution, but use unit tests (or similar) to make sure there are no regressions in the future and that it continues actually being a solution.
- ch4s3 6y agoThat's really cool. I need to revisit clojure in the near future.
- emidln 6y agoI literally wrote code in .clj files and then my editor can slurp that up and barf it into a repl. There was never a point where I was writing something that "only existed in the repl" such that I couldn't just put it in a unit test if it needed formalized. The usual workflow was something like (comment ;; experiments go here (defn add [a b] (+ a b)) (= 2 (add 1 1)) (= 3 (add 1 2)) (= 99 (add 9 90)) ) Once you have what you want, you move the code out of the comment block. Whether you move it into a library or a test file is just dependent on the type of thing you're moving. Functions moved into library code. The samples that proved the happy paths (or unhappy paths) would go into test files using `clojure.test/is` (and typically a direct copy/paste).
- lvh 6y agoThe REPL in Clojure (and other Lisps) is far more tightly integrated with your editor environment. When I wrote python, I would do what you're describing: test something in (i)python, write some code, rinse, repeat. With Clojure, I Write said experiments in the same place my code already is (and will end up). Making that feedback loop 10x tighter makes a world of difference in UX: I don't think it helps insight to equate "typing things in irb" with "mashing C-x C-e until the expr does what you want". (I have no idea if an equivalent workflow exists in Elxir, but I've never seen it been done that way: just like people use ipython, they use iex in my experience.)
- TurboHaskal 6y agoSome people really can't stop making up new terms.
- mac01021 6y agoSome people really can't stop discovering new concepts?
- frou_dh 6y agoI think there's a big distinction in the general developer population between: a) Knowing both how to effectively write tests & how to effectively use a REPL. and: b) Knowing only how to bang around in a REPL enough to use it as a crutch to avoid writing any tests.
- lvh 6y agoWhen you say "use a REPL", do you mean something like CIDER/SLIME, or do you mean something like ipython/irb? I do REPL (that is: CIDER) driven development all the time, and most of my experiments turn into tests quite naturally.
- zelphirkalt 6y agoThis is a good point. In parenthetically (it is also about not using statements) inclined langs it is easy to select from start to end of an expression or subexpressions. I never feel that ease when working with a Python REPL, while it is a joy to work in Geiser usually. REPL is definitely not REPL.
- gmfawcett 6y agoThe distinction is a bit artificial. I agree that Python's REPL is underpowered when compared with SLIME. But it's trivial to write up a bit of Python code in an editor, and just 'reload(modname)' or 'exec(file(...).read())' from the REPL. Emacs' python-mode (like many other editors) also supports "evaluate this expression / line / block" to make this process simpler. YMMV, but my Common Lisp development really doesn't look very different from my Python work.
- lvh 6y agoEven if we stipulate that's true[], saying you could do it in non-Lisps doesn't detract from the observation that virtually nobody actually does it, while both groups still call it "REPL". [] I really don't think it is: reload(mod) comes with way more caveats than C-c C-k in CIDER.
- stepbeek 6y agoThis feels like a straw man. Or maybe a false dichotomy. I think it's entirely possible to ensure code is testable before writing the test. It's also not unusual to tinker in a repl before encoding the functionality as a test. Pretty let down by this post.
- ARandomerDude 6y agoClean Code was one of the first books I read professionally, and I'm glad I did. Since then, I've formed the opinion that "Uncle Bob" sometimes (as in this case) takes helpful ideas a bit too far. But I'm still open to correction. Does anyone know of an open source codebase he's written or been a major contributor to? I know he writes a lot about clean code, but I've never seen production code from him.
- jdlshore 6y agoI believe the FitNesse codebase is open source. The core Fit code was written by Ward Cunningham, but the larger code would have been Bob and co.
- nojito 6y agoClean Code is extremely overrated and it’s hard to get justifications for the recommendations anymore. Pretty sure he hasn’t written prod code in over a decade.
- cjfd 6y agoYes, especially where he goes completely overboard creating extremely small functions. Most of them are, like, 2 lines.... The general point that code should be clean is a good point to make. Writing a whole book about it seems a bit much.
- nojito 6y ago>Yes, especially where he goes completely overboard creating extremely small functions. Most of them are, like, 2 lines.... Yup which is why we have millions of npm packages that do literally one thing.
- capableweb 6y agoTwo completely different things. A npm package is a module of code. A function is... A function. Having a lot of small functions is helpful as it builds up a vocabulary in the codebase, so instead of having to understand every single line each time you come across it, you can extract the meaning into a name. What the npm ecosystem is doing is a completely different thing.
- cjsaylor 6y agoI rarely use REPL driven development, however in my limited experience using it, it only makes integration testing faster, not unit testing. I used this methodology with a Slack bot, because doing integration testing with Slack is annoying when I'm not directly testing the Slack communication portion. For example: https://github.com/cjsaylor/chessbot/blob/master/cmd/repl/main.go https://github.com/cjsaylor/chessbot/blob/master/cmd/repl/ma... Edit: It seems like there are a lot of comments surrounding him "abandoning" TDD. Not only did he not do that, he literally says in the conclusion of the article that he would not recommend doing it.
- capableweb 6y agoWhat languages have you tried REPL driven development with? As far as I know, Golang doesn't have a REPL that is connected with your editor so you can evaluate inline code and Golang is not made for REPL driven development either, so you have bunch of implicit state and you can't overwrite function definitions at runtime either. I think you need to have a language (or structure your particular program in a way) that is really made with live-coding in mind for it to actually give you any benefits, like Smalltalk or Clojure and similar. Otherwise it's just adding another step to reach the real program you're running. Edit: using your example as an example here, hope you don't mind. A program made with REPL driven development would have way less code in the main function, in order for you to test the code outside with your REPL. The `StoreGame` function would accept a data-store + the argument you have now, so at development-time, you can pass it arbitrary stores. And similar changes.
- cjsaylor 6y agoI don't mind the criticism at all. I don't use it often enough to claim expertise. Fair points about my usage. You can still get proper editor support with Golang (even though it isn't designed for it). I've done so here with VSCode working with a "remote" delve session: https://github.com/cjsaylor/chessbot/blob/11e1059aa77fed84d20f03ce2cad06bb87e313da/.vscode/launch.json#L7-L18 https://github.com/cjsaylor/chessbot/blob/11e1059aa77fed84d2...
- 6y ago
- skratlo 6y agoIt's not REPL-driven design, it's REPL-driven development. You don't design by driving a REPL, that would be stupid. It seems Bob has lots of misconception and is blaming his poor design on the REPL. Haven't read such a hollow article in a while.
- wglb 6y agoI agree with hollow. But TDD is not design either, in my opinion.
- deleted 6y ago[deleted]
- blunte 6y agoYou still write tests in any non-trivial Clojure codebase, and you still probably do REPL-based development for some parts of that project. And if you're doing both of those things above, then you almost certainly run some or all of your tests WHILE doing REPL-based development from your always-on REPL. In fact, that's one of the best ways since you don't suffer the JVM startup cost.
- snicker7 6y agoThis is sort of like how I develop using Jupyter notebooks. Write code in the notebook until I get a function / component correct. Copy it into a module. Restart the kernel and import the module. Rinse and repeat.
- nerdponx 6y agoYup, a notebook is a REPL with better scrollback + the ability to take notes alongside your code.
- smabie 6y agoExcept unlike a REPL, all your code is still there so that you eventually find yourself in an unreproducible state that on the surface looks reproducible. Referencing variables that don't exist in the notebook (only to be caught when run by someone else), cells that need to be executed out of order, etc. The problems go on and on. People sometimes go days or weeks without restarting their notebook and the amount of "shadow" state is remarkable. I think notebooks are useful (though only for exploratory analysis), but there has to be a better way.
- EForEndeavour 6y agoPart of the problem is discipline and avoiding anti-patterns. It's unfortunate that Jupyter encourages this kind of inconsistency to creep in, leaving it up to the analyst to maintain top-to-bottom "runnability".
- nerdponx 6y agoYou can go for days and weeks without restarting your REPL. It's literally the same problem.
- samatman 6y agoThere's no reason for this, and it bothers me enough that I'm doing something about it. A (series of) REPL commands is just a test, where acceptance and rejection is manual: we look at the output, and keep changing the code until we get what we want. At which point, we should be able to say to the REPL: yes, this result is correct, this is a test, and it's now called "Thing does what I want when I give it X and Y". The runtime then saves this as a test, and will run it automatically when you ask it to. If there's a regression, it will boot back up into the interactive REPL so you can inspect it and fix it. I've been writing a REPL. Turns out that's a lot of work, but I'm getting to the point where I can add this feature, which is the last one I really want to add before I go public with this thing.
- danenania 6y agoThe trouble is that for the repl “test” to be reproducible, you don’t just need to store the relevant commands and result, but the full state/history of the repl. Since the point of a repl is to explore and experiment, it seems like your tests would end up with a lot of noise in them... but perhaps there are ways to get around this? I agree the concept of quickly converting repl sessions to tests is very compelling.
- samatman 6y agoyes, there's a lot to it! I store the complete repl history, complete with results and session metadata, in a database. Pruning a session's history down to only what's needed is a manual process, true, and laying the groundwork to provide that feature has been the main yak shave.
- bch 6y ago> ...manual process, true Before I get to my real comment, I guess a fair criticism of it would be “Well, where in the [development process] hierarchy do you actually want to live?”, which I guess is a longer discussion, but as a developer, you can’t become battle-hardened without going to battle. So we can nod to Carl Sagan[0] and say “I made this from scratch”, and know there’s value in that. At least occasionally running your REPLs by hand and taking the actual trip, instead of just wanting the destination. It may not be tenable as a full-time production development strategy, but that doesn’t mean it’s worthless. [0] https://youtu.be/7s664NsLeFM https://youtu.be/7s664NsLeFM
- jskulski 6y agoBob Martin is myopic in his view of software, in a way that I think is harmful to teams and the industry. It is my experience that good software comes from teams working together well, teams that are in some sort of harmony with each other AND the system they are building/using. They understand it and each other. Good software is not derived from a singular technique. I do like test-first approaches when I can, I like SOLID. I probably agree with Bob on a lot, actually. I think they are good techniques to employ. However, it has not been uncommon in my career to find people (and myself) getting dogmatic about these things and arguing over them. There are half baked attempts to change everything to an Entity. There are flaky test suites with a lot of mocks because someone is hot on TDD, but does not have the bandwidth to maintain the suite for the whole team. This blogicle is another example. As pointed out by other posters, REPL driven development can fold VERY well into a test driven workflow. However, that's not enough, he's actively saying it's not as good and you need to do The One True technique.
- throwaway894345 6y agoI think this is true in general. There are very few "always right" ideas in software development--virtually every choice is a function of a broader economic context. For example, if you work for a startup and you need to determine market fit quickly, it's more important to move fast than to have very high degrees of quality (although below a certain threshold, low quality impedes velocity) or performance. Economic context affects virtually everything, and the difference between good and great software engineers is the ability to understand economic context (and changes in economic context) and the technology/process ramifications.
- ironmagma 6y agoYou can’t test everything. To believe we can is just hubris; even if we get every function to have coverage, we won’t have coverage of the full range of inputs unless all that’s being done is some very simple programming. The combinatorial explosion is fast. Testing definitely has value, but when testing you always have to make the assumption that you can’t test everything, and then the trick is deciding what to test versus what not to. I think people forget this and focus on having every function tested, every interface having coverage, etc.
- phoe-krk 6y agoThis is a PEBKAC, not a problem with REPL-driven development. He had all the test bodies written as input in the REPL, he had all the test results printed as output in the REPL. Why didn't he turn these into unit tests as he programmed? That's a simple lack of foresight on the programmer's side that is then blamed on the tools that were used.
- shay_ker 6y agoUsing a REPL has been critical for me to develop any meaningful software (more than tests and more than types). I always wanted to be able to take a portion of my REPL history and transform it into a test, instead of basically rewriting it (lazy, I know).
- gumby 6y agoThis was the programming model I learned on the lisp machine and Common Lisp: define a few datastructures and some operations on them, explore them with some live data, and expand. All iteratively at the REPL. A few years later a friend referred to this dismissively as “Programming by successive approximation.” There is a lot of truth to this, but the distinction isn’t as sharp as it sounds: you still have to think ahead, and as you explore and expand you’ve typically always got something working, some test cases and invariants, and typically you have the benefit of working on live data. Even though I do most of my work in compiled C++, my work model isn’t that different.
- whalesalad 6y agoI always have a REPL open when I am hacking. iPython is a great replacement for the vanilla Python one, and Pry is great for Ruby. Being able to poke and prod at a system is priceless for debugging and introspection.
- brlewis 6y ago> So I’ve learned my lesson. REPL driven development feels easier and faster than TDD; but it is not. Next time, it’s back to TDD for me. In the long run it's not faster. However, the feature he was using REPL driven development for did ship faster. That's a valid tradeoff to make, as long as you're doing it consciously.
- kitanata 6y agoIs this just the equivalent of saying "Hey, REPLs are nice when you're trying to figure something out", but spouted off as some deep programming wisdom? Like, folks... REPLs are really nice when you want to figure out how a function or module or whatever works. Then yeah... you can take that knowledge you gained and apply it to your work. Like.. how is this a deep profound revelation for folks?
- hartator 6y agoDo both? Lot of manual and automated testings?
- meddlin 6y agoWhat's the difference? And why does executing commands (even if you're actively developing the code they execute) in a terminal need a new name? Isn't this just...using a computer? I don't know; maybe I missed something.
- Jtsummers 6y agoREPL isn't new. It's an old term, coined I don't know when but certainly a long time ago as Lisps have used it for decades. And yes, your shell or terminal is a REPL.
- michaelmrose 6y agoIt needs a name so that we can have this discussion and compare and contrast different methodologies. For example * Compile run debug. You write some code potentially with tests. Run it and either poke it manually or run tests to see what it did. If it didn't do what was expected you either edit it, write some more tests, add print statements, add break points or whatever and try again. You are forced to create all state in one go and pay one complete edit build debug cycle for each thing you want to modify in isolation. This could be 30 seconds or hours. * Minimal interactive development. You mostly write code as above but gain the ability to poke individual functions or pieces of functionality in order to aid your understanding when things don't work correctly. You write tests eventually or not. * Repl driven development. You mostly write and interact with a live environment either by actually typing the code in a repl or writing it into a source code file and hitting a shortcut to evaluate as you make changes redefining it piece by piece in terms of the rest of the code and the state you have built up by prior evaluations. This persistent state and the ability to redefine parts of it are what separates it from writing some python and occasionally opening up ipython to poke the code.
- stopachka 6y agoIm not sure I agree with the conclusion. Why not compose with the repl, then write tests at the end? The common fear hear is that “you be motivated to write the test, or the test will be less good” For motivation I never found it to be a big issue, and for the quality of the test, writing it after often produces better results. You get more time to think about _what and how_ you want to test
- wglb 6y agoBob Martin, who is quite opinionated, has been brutally wrong about some things. For example, there was a TDD attempt to solve Sudoku that failed. Uncle Bob was following this, and said of the failure "At least he had the courage to post his failure". More interesting would be a correct solution, which was posted by Peter Norvig. The approach he uses is drastically different that anything TDD is likely to get for you. TDD isn't going to find solutions to hard problems beyond bowling. There is a stark contrast between agile development and actual software engineering. Agile works where the customer and the developer don't really know what is being developed. And as much as he has lectured about software development, I don't get the feeling that he has actually developed a substantial quantity of serious software. In one of his clean coder books, he argues that Java is not object oriented. Unclear if that is a widely-held opinion. If you look for serious high-quality software, they are not agile. Such as the software for the space shuttle, or qmail, or other formally-verified software. I have a friend who worked with him and it was sort of Uncle Bob's way or the highway.
- voldacar 6y agoLiterally an anti-hacker. Not sure why his writings get upvotes here
- justincredible 6y agoA methodology analogous to biological evolution leads to problematic designs? Shocking.