Hi Timur, I find almost all of my pain is from changing shapes of the data that flows through the system. As you say “I pass the results in a map and each function updates the map” so even if you unit test each fn that doesn’t actually help because the tests provide the (now incorrect) data. Stepping back a problem this is a problem of visibility and enforcement - the required structures are opaque (otherwise it wouldn’t be painful) and they aren’t enforced.
On a related note, I don’t see why you can’t still write atomic tests here... I find Prismatic Schema a great answer to both of these. I also think it is worth viewing fns as a graph rather than a list - I frequently have a fn which then delegates to a bunch of other fns. Add a few of the ‘top level’ fns in a single namespace and it gets really hard really quickly to see this graph making it hard to visualise an impact analysis. Again, one trick is to document these ‘gateway’ fns with the schema and simply use pre/post (or not, depending on the complexity) on the ‘inner fns’. Namespaces are also an obvious tool but I don’t think one ns per high level fn is viable. I also find in that function graph at the bottom are ‘shape-a->shape-b’, in the middle are higher level fn which takes some sequence or graph, untangles it and then maps/reduces the ‘bottom’ shape changer fns. At the top are higher level/entry point fns which are more often than not push a data structure through a sequence of the ‘middle’ fns using (-> ) or (->> ). I deliberately don’t document anything other than the high level fns as a starting point - everything else should be _so_ obvious (assuming you understand Clojure of course) that a ‘what does this do’ is a smell. The higher level fns are documented in terms of the schema (which with good naming is self-documenting) and _why_ it is doing what it is doing. Another cause of pain is the cognitive load of the code itself. Initially I found myself writing so much code that was in the core library but I didn’t know it. Once I learnt the core library it wasn’t unusual to delete 10s of line to replace with a 1 or 2 liner using the core data structures/idioms. This has a non-obvious benefit which is a non-trivial amount of code is ‘just’ using the core fns. This means there is nothing new - the core fns become a significant part of your code’s ubiquitous language. I recall a senior member of the community saying something like ‘whenever I write my own code I consider it a smell’ (or something like it). The point is familiarity - the more core fns you use the more domain-agnostic and familiar that code will be. My final point (waffled too much already I think) is that of ignorance - you want your fns to be as ignorant as possible. For each fn follow this process _ruthlessly_: - what assumptions does this fn make? - what can change outside this fn which will break this fn? - how many times do I use the word ‘and’ when I describe this fn - can I replace it with a core fn For the higher level fns the answer is usually ‘many ands’ because they implement a process but that is fine. Once you are happy that your code is composed of tiny little ignorant and bullet proof bricks, all composed together in readable top level fns and feeling all great about it, go get somebody to review it, even if you have paired on the code, always get somebody else to read it, not for correctness, just for consistency (using same idioms, style etc.) with the rest of the code base. All of this is to say the pain comes from obfuscation, I find the above goes along way to writing self documenting code. In summary, what I find helpful: - the pain comes from the opaqueness of the really important concepts: data shapes and function graphs - comments explaining _what_ the code are doing are really helpful when you are writing fns that do too much - Prismatic Schema to document _and_ enforce (runtime enforcement isn’t really that inefficient I find) the abstract shapes - fns further up the graph tend to be very well documented, non-private and almost certainly have a schema attached - delegate fns tend to be trivial one or two liners that typically map/reduce something over a sequence - actual worker fns tend to take one element of a sequence and are likely to be `defmulti`s. These take one fairly trivial ‘shape’ - always get somebody else’s eyes - they might be able to say ‘why’ the code is doing it but they should be able to say _what_ the code is doing - WITHOUT you prompting them :-) HTH - knee jerk and written stream of consciousness so apologies for numptyness > On 5 Dec 2015, at 14:10, Timur <timurha...@gmail.com> wrote: > > Hi all, > > I'm using Clojure to build a set of services. > > Development with Clojure is fun but most of the time, I lose time with fixing > regressions due to changing small parts of the codes. Do you have any best > practices to avoid regressions occurring due to changes in the code? I use > :pre and :post conditions. Sometimes, they are not sufficient. do not write > so far regression tests as they are quite time consuming. Writing these tests > will be probably an answer, I guess. Do you have any guidelines to write good > regression tests? It would be nice if I could re-use the code that I type > into the REPL to test separate functions, into the test code but most of the > time it somehow does not work. Another problem I'm facing is many functions > are dependent on each other, so I cannot test each separately, I pass the > results in a map and each function updates the map, so I cannot write really > atomic tests. > > Any suggestions? > > > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with your > first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > <http://groups.google.com/group/clojure?hl=en> > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com > <mailto:clojure+unsubscr...@googlegroups.com>. > For more options, visit https://groups.google.com/d/optout > <https://groups.google.com/d/optout>. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.