Fork me on GitHub
#clojure-uk
<
2020-11-23
>
dharrigan08:11:34

Good Morning

djm08:11:00

šŸ‘‹

codeasone08:11:03

Morning šŸŒ…

Aleksander08:11:58

Morning everyone!

zyxmn09:11:52

Good morning!

dharrigan09:11:28

I came across this morning, and didnā€™t realise that it would be possible to conduct side effectful testing (i.e., attempting to call a db, or a website API) using protocols to encapsulate the external call. Anyone else use shrubbery?

mccraigmccraig10:11:35

i've find using protocols to encapsulate external interfaces very useful for testing and shrubbery looks like it would make life easier. shame it's not supporting cljs too though

dharrigan10:11:11

interesting. I don't use cljs, so I must have a play and see if it will work for me šŸ˜‰

dharrigan10:11:25

(in my testing on the backend code that I maintain)

lsnape10:11:12

I also find protocols for external interfaces massively helpful when testing, and it forces more structure on the codebase too.

agile_geek10:11:29

Iā€™ve used shrubbery briefly when I was playing with Duct. As Duct uses protocols for external dependencies by default.

agile_geek10:11:55

But that was in ā€˜toyā€™ projects as none of the clients Iā€™ve worked at use it

gcaban11:11:09

re: protocols to encapsulate external calls, isnā€™t it what the good old https://en.wikipedia.org/wiki/Hexagonal_architecture_(software) advocated?

lsnape11:11:13

had not heard of Hexagonal architecture ā€“ TIL! Iā€™m surprised the idea is even that new (2005)

gcaban12:11:32

arguably itā€™s an even older idea or practice with new-ish name. But I found this way of thinking useful from time to time, especially asking myself ā€œIs this piece of code part of my Application or is it something that should be wrapped in one of those ports or adapters?ā€œ. I think it helps prevent your architecture being driven by frameworks or libs youā€™re using.

šŸ’Æ 9
dominicm14:11:19

You'll get mixed answers about whether this is idiomatic clojure or not in my experience :)

lsnape16:11:49

Interesting. Dominic, whatā€™s the nature of the argument against using protocols for external calls? Choosing the right abstraction sometimes requires a bit of thought, and for a very small service that has maybe one dependency it might be overkill I guess.

dominicm16:11:58

The arguments are usually along the lines of: * by the time you've written all your mocks, what are you even testing? * why is your code under test not just a function that takes the stateful result and processes it (which doesn't require a protocol to test)

lsnape16:11:08

By code under test do you mean the application code that interfaces with the protocols, or the protocols themselves? If I understand correctly the former should be functions all the way down. I agree that testing anything the other side of the protocol is built on assumptions about how that dependency works, its failure cases etc.; thatā€™s why I usually advocate for them being as thin as possible.e

dominicm16:11:45

I mean that instead of:

(defn foo [slack]
  (let [users (get-users slack)]
    (map inc users)))
Why is it not written
(defn foo [slack-users]
  (map inc slack-users))

(defn some-composing-fn
  [slack]
  (foo (get-users slack)))

dominicm16:11:52

where composition isn't tested

šŸ‘Œ 3
lsnape17:11:49

Either way, get-users could be a function or a protocol in this example. But this touches on another important point: what the order of invocation should be to best facilitate testing. For that the latter wins.

dominicm17:11:43

Yep. There's often no true scotsman arguments in this space, especially around integration tests and mocks. Personally, I flip flop on this. Right now I'm not as keen because I'm finding it's quite hard to mock out our ever-growing use of the slack API, and I wish I'd never started testing it that way.

mccraigmccraig17:11:27

i like our routing which is all data -> fn -> effect-data and is very easy to test without any mocks... but our push-notification service has logic around retry and other behaviour when errors are returned from the external service, and that seems to test nicely with mocks :man-shrugging:

seancorfield17:11:23

I'm one of those who is against wrapping all the external stuff in protocols. It's looks fine in small examples where the protocol only has one or two "methods" but if you've got, say, 100 database tables and your code has to do a variety of CRUD-like operations on each one, now you have maybe 100 protocols, with 200-400 "methods" in total, with separate declarations and implementations, and the supposed benefit is to be able to reimplement those 200-400 functions with mock behavior so you can test code without needing the external systems around. That seems like a huge amount of boilerplate and duplication to me.

seancorfield17:11:31

Stuart Sierra made an argument in favor of protocols and separate test/production implementations around his Component library and, again, the examples he showed were all simple things: a few protocols, each with only one or two functions, and it looks fine at that scale... but even though that was part of his "Clojure in the Large" talk about structure and (code/complexity) scale, he didn't show what a "large" system actually looked like with that approach and I've never seen that approach used in "large" codebases.

seancorfield17:11:21

I think a better approach for testing code that ultimately does CRUD against a database is to have a separate test DB, preferably in memory if you can find a common SQL dialect between that and your production DB. We use MySQL (Percona) at work so we test against a local scratch database with SQL migrations to build it up from nothing (if needed). If we were using PostgreSQL, I'd probably have our test DB in memory via Embedded PostgreSQL (which I use when testing next.jdbc).

mccraigmccraig17:11:31

oh, i definitely agree with you there too @U04V70XH6 - all that boilerplate would be awful

mccraigmccraig17:11:27

for our db CI we run up a fresh db server in a container... i never managed to make an in-memory cassandra instance work very well

lsnape17:11:02

I can relate somewhat to what Sean and Dominic have experienced. The codebase for one of our mature services has maybe 4 or 5 protocols each containing several methods and that can be a chore to mock. We have multiple protocols defined for the same store but grouped by entity (e.g. UserStore, AnswerStore), which goes some way in avoiding behemoth protocols. I have felt the pain of the boilerplate. It means the tests have less chance of being written, or taking longer to write, time which could be spent writing more tests.e

seancorfield17:11:32

Yeah, we run docker-compose to spin up ElasticSearch, Percona, and Redis locally for testing. That way no one has to maintain any infrastructure and the Docker yaml file is part of the repo, along with scripts to fully populate the system from a cold start.

seancorfield17:11:36

@U07SQJ620 I just checked and we have over 300 DB tables at this point... the number of protocols and methods for mocking that lot... šŸ‘€

šŸ˜± 3
lsnape17:11:45

One other thing I like about protocols is that they are distinct from plain-old pure functions. I really liked the caveats in DDIA that Kleppmann makes about RPCs, that they behave like a function in the happy path but can fail it all sorts of ways that a pure function direct call canā€™t. I think the same point applies to external dependencies.

lsnape18:11:37

Although I appreciate protocols are almost indistinguishable from functions at the call site šŸ™‚ (you might be able to infer from the args)

dominicm18:11:27

The real solution here is a pure Clojure implementation of MySQL

seancorfield18:11:42

Another potential downside to protocols: you can't instrument them so you need to write wrapper functions if you go down that path.

seancorfield18:11:13

(and they can't be variadic, right? so you'd need wrapper functions for any operation that you wanted to be variadic, even if you didn't want to instrument them)

dharrigan09:11:41

(or the approach of using protocols to wrap external calls)

danm10:11:17

Morning!

danm10:11:31

I keep forgetting to say hi to folk on here until it's almost the end of the work day for me. But I start a new job in Feb and back to doing Clojure, so time to get back into the community and extricate myself from all the Python Data Engineering šŸ˜„

parrot 3
šŸŽ‰ 3
clojure-berlin 3
alexlynham11:11:07

@carr0t where are you going to be working?

danm11:11:51

Remote working Software Engineer/DevOps/SRE for Griffin, a bank startup with Clojure and Kafka core in their stack. I believe from what others have said before that they might have posted in here (or another channel in the workspace) as part of recruitment

alexlynham11:11:00

congrats on the new gig

āž• 3
danm11:11:58

Ta šŸ˜„

Mario Giampietri11:11:03

+1. It's always great news when somebody switches to a Clojure role šŸ˜

cddr12:11:56

Morning.

jiriknesl13:11:39

How do you onboard new Clojure devs? Before, we have been putting them to running teams and asked colleagues to help them. But now, we got to a scale, where we are basically getting to the point, where we will have to run ā€œClojure kindergartenā€ (sorry for not having better name for that) as weā€™re going to onboard 10-15 new Clojure devs in following 60 days.

dharrigan13:11:26

wow 10+ devs

dharrigan13:11:43

even leaving out Clojure, onboarding that many devs is always a challenge

jiriknesl13:11:54

Yes, we have some 8 Clojure teams, going to 10 and making some of existing ones stronger. A lot of maps, filters & things to teach and hand over.

alexlynham13:11:01

how many existing devs have you got?

alexlynham13:11:17

1-1 buddying is your best bet if they're new to clj probably

jiriknesl13:11:35

In Clj, it is around 40 I think.

alexlynham14:11:57

make sure everybody has somebody they're comfortable dropping a line to, to ask them questions for starters make sure the people doing the mentoring have the time to spend with the new people suggest that people put in an hour a week to catch up with the person they're paired with?

alexlynham14:11:01

idk this stuff is hard hard

alexlynham14:11:14

very 'you can lead a horse to water' kinda territory

alexlynham14:11:26

esp as everybody learns very differently

jiriknesl14:11:16

Yes, this is what we did until now. Have a newcomer in the team and all extroverted devs taking care of them. But on this scale, we are thinking of really a period where there is a few weeks spent just by learning, not by being in any particular team.

gcaban14:11:13

Interesting question. You could perhaps draw some inspiration from other companies that have that problem, twitter and guardian run a ā€œScala schoolā€ for new joiners and Iā€™ve heard of something similar for Ocaml at Jane Street. Link to twitter materials https://twitter.github.io/scala_school/index.html .

yogidevbear16:11:45

Appsflyer do the same thing for Clojure šŸ‘

jiriknesl14:11:01

Thank you! This is exactly what I am looking for, just for Clojure.

gcaban14:11:01

But I agree with @alex.lynham, no syllabus will replace working with someone experienced. Maybe let them build some internal tools (or just toy projects), but on real infrastructure and under supervision ?

jiriknesl14:11:18

We plan to have experienced devs to be with them.

alexlynham14:11:11

one of the north american clojure shops used to do a clojure bootcamp

alexlynham14:11:24

they def had a blog post & open sourced the materials

Aleksander14:11:08

Would they typically have functional experience from other languages?

jiriknesl14:11:21

@dev964 all of them have at least pet projects in Clojure, but from my experience Clj devs recruit from other functional languages, Ruby or JVM-land (and most often Java).

zyxmn14:11:48

Nice to see clojure training being offered, I was just thrown in the deep end to float or sink.

alexlynham14:11:07

similarish, i had a project an a deadline and then just had to crawl to rick for help when i hit a brick wall

3
alexlynham14:11:35

although from memory i was learning emacs at the same time and often my issues were tooling (repl/versions/emacs config)

alexlynham14:11:03

coming from ruby/js the JVM was a big overhead of stuff to know to get the project and repl building smoothly and keeping everything ticking along

alexlynham14:11:41

and this was... 20...14? so some things had sharper edges from memory. maybe i just don't feel that pain in the same way cos i know the gotchas, idk

jiriknesl14:11:15

I get you. I was switching from Erlang and LiveScript without preexisting experience with Lisps, JVM, Vim user, somehow around 2012 I remember and there were lots of rough edges.

Edward Hughes15:11:36

I wound up having to teach someone who was hired before me the Clojure ropes at the last place I worked, and I was basically left to onboard myself, though I guess I have the opposite experience to most being more familiar with Lisp than with how the industry operates. I'd market myself as a trainer but I don't have the experience to make that stick šŸ˜…

Edward Hughes15:11:19

Been trying to keep myself busy and train up that particular muscle by teaching non-professional interested friends and reflecting on the pedagogy.

alexlynham15:11:09

i think that particularly with clojure there's a not insignificant element of it needing to fit with your mindset

Edward Hughes15:11:58

I feel like there's a certain minimalism coupled with a desire for aesthetic elegance that seems to be kernel of the "Lisp mindset". At least I've found that folks who really to take to Clojure seem to lean that way. The sort of Daoist approach to cognitive flow is also something I've found to be a hurdle in that the language is very much informed by it, but the subtlety is easy to miss if you're not looking for it. Can't appreciate the forest for the trees, as it were.

alexlynham15:11:20

it seems to click more naturally for some than others

alexlynham15:11:23

like, if you show haskell or whatever the hangup is usually the complexity of category theory, not the essentials of FP, as let's face it, most decent JS devs are comfy with FP these days

alexlynham15:11:36

there's something specific about lisp that is a hurdle possibly

gcaban15:11:34

but that Lisp self selection has already occurred if they all have clojure pet projects. They just need experience with bigger codebases and a particular tech stack, donā€™t they ?

gcaban15:11:36

and thatā€™s the thing, we like to focus on programming languages, but often learning tools, libraries, runtime, infrastructure and operational processes at the company takes a lot more time imho ( I might be biased, after all I like learning new languages..). So making them build something real enough that it will go through all stages of dev process on a real codebase, ideally up to some silent prod (parallel)? release will probably go miles towards getting them to the stage where theyā€™re productive

rickmoynihan16:11:13

@jiriknesl: Iā€™m no expert; but Iā€™ve taught clojure to a bunch of people over the past 12/13 years or so. Obviously do all the stuff youā€™re already doing and the stuff @alex.lynham suggested too. However if you have 10+ new folk who you need to bring up to speed quickly Iā€™d suggest you also need to give the them the time to properly learn the basics. i.e. donā€™t just throw them in at the deep end and expect them to swim. So Iā€™d suggest the easy option might be to find them a proper training course. iirc juxt used to provide clojure training courses in the UK, though they appear to be a bit more product focussed these days; but might still be worth asking. Even a little help in educating people early on will take off a lot of pressure allowing the experienced folk on the team to still ship stuff, and bring them up to speed on the aspects that are bespoke to your product/business. Also Alex says I threw him in at the deep end; and I certainly did that šŸ™Š, but Iā€™d suggest thatā€™s only effective if the student is willing, and youā€™re willing to also teach/help on the side explain the fundamentals. It was a long time ago now, but Iā€™m pretty sure I sat Alex down at a REPL a few dozen times to explain fundamental clojureā€¦ because I think the fundamentals of clojure are the interesting subtle bits that people overlook when learning itā€¦ they think ā€œok thatā€™s how you do if āœ… ā€ ; rather than think about how if is actually a special-form and how special forms relate to macros, evaluation etc. Spending sufficient time on the basics wonā€™t necessarily feel like the quickest way to get people to build a webapp; but it will pay off many times over, and help people understand why error messages are the way they are, for example. Iā€™ve met many folk using clojure, who ignore useful clues in stacktraces for example, because they donā€™t appreciate the details of clojureā€™s construction and evaluation model etc. So thatā€™s my main tip; is spend time teaching people the language itself and working at the repl. Foster a culture of being able to debug anything by finding a small expression that recreates the problem in the repl etcā€¦ Some of that work you can probably outsource; and if you donā€™t thatā€™s fine; but Iā€™d suggest make sure you teach that in parallel to the on the job stuff.

šŸ‘ 6
alexlynham16:11:31

from memory we took a morning and made a simple compojure app in the repl and then you basically mapped the system-in-repl onto ruby/rails contexts cos that was easier to explain in terms of what the running system/program was & how to interact w/it and the repl flow :thinking_face: it was a while ago tho!

alexlynham16:11:18

i def remember having to ask a lot of qs about the JVM and emacs. lein felt like a monster compared to the ruby/js runtimes

rickmoynihan16:11:30

Ok that actually rings a bell nowā€¦ Iā€™m pretty sure whenever you hit a problem though, Iā€™d probably have backed right up and explained some fundamentalsā€¦ either to reduce the problem, or to explain what you were seeing and why.

alexlynham16:11:09

i definitely remember something about how everything is a macro

alexlynham16:11:40

cos i remember you hitting macroexpand on a load of forms and being a bit like 'wut, that's not a lang primitive' on lots of operations

rickmoynihan16:11:41

ok that was probably some api we were usingā€¦ maybe mount :face_vomiting: or compojure or the elasticsearch library :man-shrugging:

alexlynham16:11:39

yeah must have been mount

alexlynham16:11:14

cos moving to integrant was at the tail end of my swirrl days iirc

alexlynham16:11:10

but the point stands that going over the fundamentals and the repl workflow is worth doing šŸ˜‚

šŸ‘ 3
jiriknesl17:11:09

Our plan is that in the beginning theyā€™ll work on some open source (as we are way behind in open source for an org of this size) on a stack that is similar to the stack they will use.