Building a JIRA integration with Clojure & Atlassian Connect

A simple example application

Building a JIRA integration with Clojure & Atlassian Connect

Atlassian JIRA is the market leading issue-tracking and agile project management tool, and is in heavy use at companies around the globe.

For CTX, my team search app I wanted to build a slick integration to JIRA to help my customers to search their JIRA issues alongside their Slack messages, Trello cards, files, GitHub issues and emails.

When it came to actually building the integration, I was pleasantly surprised by the APIs available, and especially the Atlassian Connect API and the simple integration patterns it promotes.

While there's lots of documentation - and even a short blog series about integrating Bitbucket with a Clojure application, I couldn't find a single, simple tutorial that covered Connect, the webhook interface and the main JIRA API in one place.

So I wrote one.

The full source of the application we're building is available on GitHub here.

What are we building?

We're going to build a JIRA integration in Clojure, that listens for issues being created and mutated and creates a basic activity feed of changes.

To do this, we need a Clojure web application, listening on an HTTPS endpoint.
We'll register the application with JIRA, and every time a user creates or updates a ticket, JIRA will send us an event on a Webhook.

We'll record these in an in-memory database, and write a little front-end application that polls our app to display a list of changes to issues.
Building a JIRA integration with Clojure & Atlassian Connect

What is Atlassian Connect?

Connect provides a simple framework for integrating with Atlassian applications like JIRA and Confluence. It leverages OAuth2 and JWT, along with some simple integration patterns, to give developers a nice and consistent way to talk to the Atlassian API suite.

Building a JIRA integration with Clojure & Atlassian Connect

In its' most basic form, all you need to do is publish a Descriptor listing the endpoints you provide and the services you require, point your Atlassian application at it, and then deploy some code on the listed endpoints that talks HTTP / JSON.


First, install Leiningen, and make sure it's in your PATH.
And get yourself a Java JDK if you haven't got one.

Check it all works:

$ lein --version
Leiningen 2.8.1 on Java 9.0.4 Java HotSpot(TM) 64-Bit Server VM  

Basic project setup

Let's create a basic Clojure app skeleton using the Compojure template.

$ lein new compojure connect-example
$ cd connect-example
$ ls

-rw-r--r--  1 rory  Users  273 20 Mar 16:21
-rw-r--r--  1 rory  Users  483 20 Mar 16:21 project.clj
drwxr-xr-x  3 rory  Users  102 20 Mar 16:21 resources  
drwxr-xr-x  3 rory  Users  102 20 Mar 16:21 src  
drwxr-xr-x  3 rory  Users  102 20 Mar 16:21 test

$ lein ring server-headless

Retrieving lein-ring/lein-ring/0.9.7/lein-ring-0.9.7.pom from clojars  
2018-03-20 16:24:40.501:INFO:oejs.Server:jetty-7.6.13.v20130916  
2018-03-20 16:24:40.550:INFO:oejs.AbstractConnector:Started SelectChannelConnector@  

In a second tab, use curl to test it's running:

$ curl http://localhost:3000
Hello World  

Great! We've got a running app.

Import basic dependencies

Now we need to add some libraries to our Clojure project to let us do the basics of HTTP comms, the Connect flow and so on.

Open up the file project.clj in an editor of your choice, and make it look like this:

(defproject connect-example "0.1.0-SNAPSHOT"
  :min-lein-version "2.0.0"
  :dependencies [[org.clojure/clojure "1.9.0"]
                 [compojure "1.6.0"]
                 [ring/ring-defaults "0.3.1"]
                 [ring/ring-json "0.4.0"]
                 [clj-connect "0.2.4"]]
  :plugins [[lein-ring "0.9.7"]]
  :ring {:handler connect-example.handler/app}
  :resource-paths ["resources"]
  {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                        [ring/ring-mock "0.3.0"]]}})

The libraries in the :dependencies clause give us HTTP serving (ring), routing (compojure) and a simple wrapper around the Atlassian Connect API & underlying JWT technology (clj-connect)

Connecting to JIRA

JIRA will only talk to our application over HTTPS, so the first hurdle is how to get it on the internet.

Without setting up a server, or using something like Heroku, this can be annoyingly time consuming, so here's a simple way round it: ngrok.

ngrok lets you create an externally accessible SSL endpoint to test your integration software. You just run it like this;

$ ngrok tls 3000

ngrok by @inconshreveable    

Session Status                online  
Account                       Rory Gibson  
Version                       2.2.8  
Region                        United States (us)  
Web Interface         
Forwarding           -> localhost:3000  
Forwarding           -> localhost:3000

Connections                   ttl     opn     rt1     rt5     p50     p90  
                              0       0       0.00    0.00    0.00    0.00

and it will create a tunnel, and give you a URL - e.g. - you can use to access your software, running on your development machine but accessible over SSL on the internet.

Now that we have a service that's discoverable externally, we can generate our OAuth credentials in JIRA.

Serving the Descriptor

Now we've got all the plumbing sorted, we can serve up the Connect Descriptor.

This is a simple JSON file that declares the application's capabilities and dependencies, so that JIRA (and other Atlassian applications) know how to talk to it.

Create a file at <project>/resources/public/connect.json and paste the following content into it (ensure that the baseUrl property matches your ngrok URL):

    "key": "connect-example",
    "name": "connect-example",
    "description": "Demo JIRA / Clojure integration",
    "baseUrl": "",
    "authentication": {
        "type": "jwt"
    "lifecycle": {
        "installed": "/installed"
    "modules": {
        "oauthConsumer": {
            "clientId": "PLACEHOLDER"
        "webhooks": [
                "event": "*",
                "url": "/recv"
    "scopes": ["READ"]

Handling lifecycle callbacks

The Connect API works by loading your descriptor, then making a call to /installed on your app as a "lifecycle callback" (there are others, like Uninstalled, that we're ignoring here).

All it wants is a 200 response.

We'll use Compojure and Ring to create a simple handler that serves this function (and provides basic API plumbing)

(ns connect-example.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults api-defaults]]
            [ring.middleware.json :refer [wrap-json-body wrap-json-response]]
            [ring.util.response :refer [response]]))

(defroutes app-routes
  (POST "/installed" [] {:status 200 :body "OK"}))

(def app
  (-> app-routes
    (wrap-defaults api-defaults)))

Now that we have this much, we can wire it up to JIRA in the Cloud. Save everything and restart the app (lein ring server-headless)

Go and login to your JIRA instance and click Settings (the cog icon) > Add-Ons.

Go to the tiny Settings link at the bottom of the page.

Building a JIRA integration with Clojure & Atlassian Connect

and ensure that Enable development mode and Enable private listings are both ticked.

Now you should see a link to Upload add-on - click this and paste in the fully-qualified HTTPS URL to your descriptor (e.g.

Building a JIRA integration with Clojure & Atlassian Connect

Handling events

Once Connect has hooked up to your app, you'll start receiving the events we specified in the Descriptor whenever they occur.
All we need is some handlers and some logic on our side.

Add 2 routes to the routing block - one for JIRA to call when an event happens, and one for us to call to list events.

(POST "/recv" req (recv req))
(GET "/events" req (response (events req)))

and the functions for them to call.

recv gets the payload, extracts the data we want and stores it in an atom in the server (instead of a DB, this is only a demo :)

(def event-store (atom '()))

(defn persist!
  "Save event to local in-memory structure"
  (swap! event-store conj e)
  (println e) ;; debug info for development

(defn transform
  "Get the fields we care about from the JIRA payload"
  (let [id        (-> e1 :issue :key)
        user      (-> e1 :user :displayName)
        timestamp (-> e1 :timestamp)
        type      (-> e1 :webhookEvent)
        summary   (-> e1 :issue :fields :summary)]

    {:user user :id id :timestamp timestamp :summary summary :type type}))

(defn handle-event
  (-> raw

(defn events

(defn recv
  "Handler function for all events"
  (handle-event (:body req))
  {:status 200 :body "OK"})

Restart your app

Hit Ctrl-C then lein ring server-headless again.

If you change an Issue in your JIRA (say by moving it from To Do to Done in the agile view) you should see some output in your terminal as JIRA calls our app and we save the data, and dump it to screen with the println above.

Hit the /events endpoint to check we're saving the data and you should see something like this:

$ curl http://localhost:3000/events
[{"summary":"My issue 1,"timestamp":1521891438142,"type":"jira:issue_updated","id":"TEST-1"}]

Adding a UI

That's great! We have an inbound and outbound API.
But most non-developers don't like to use curl for everything, so we'd better add a UI.

We'll build something in ClojureScript, so that we can use the same paradigms and dialect across front and back-end codebases.

$ mkdir -p src/clj
$ mv src/connect-example src/clj
$ mkdir -p src/cljs/connect-example
$ touch resources/public/index.html

Now edit your project.clj to the following (pulling in the new directories and adding the ClojureScript libraries and build configuration)

(defproject connect-example "0.1.0-SNAPSHOT"
  :min-lein-version "2.0.0"
  :dependencies [[org.clojure/clojure "1.9.0"]
                 [org.clojure/clojurescript "1.10.217"]
                 [compojure "1.6.0"]
                 [ring/ring-defaults "0.3.1"]
                 [ring/ring-json "0.4.0"]
                 [rum "0.11.2"]
                 [clj-connect "0.2.4"]
                 [cljs-ajax "0.7.3"]]

  :plugins [[lein-ring "0.9.7"]
            [lein-figwheel "0.5.13"]]

  :source-paths ["src/clj"]
  :resource-pahts ["resources" "target/cljsbuild"]
  :ring {:handler connect-example.handler/app}
  {:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
                        [ring/ring-mock "0.3.2"]]}}

  :clean-targets ^{:protect false} [:target-path "out" "resources/public/cljs"]

  :figwheel {:css-dirs ["resources/public/css"]
             :server-port 3000
             :ring-handler connect-example.handler/app}

  :cljsbuild {
              :builds [{:id "dev"
                        :source-paths ["src/cljs"]
                        :figwheel true
                        :compiler {:main "connect-example.core"
                                   :asset-path "cljs/out"
                                   :output-to  "resources/public/cljs/main.js"
                                   :output-dir "resources/public/cljs/out"}

Create and fill in your resources/public/index.html

<!DOCTYPE html>  
    <div id="content"></div>
    <script src="app.js" type="text/javascript"></script>

and src/cljs/connect-example/core.cljs...

(ns connect-example.core)

(let [el (.getElementById js/document "content")]
  (set! (.-innerText el) "Hello World"))

Which will, when it's loaded, find the div with ID content and put an informative message into it.

Now let's try to run it, using the awesome Figwheel plugin;

$ lein figwheel

Retrieving figwheel/figwheel/0.5.13/figwheel-0.5.13.pom from clojars  
Retrieving org/clojure/core.async/0.3.443/core.async-0.3.443.pom from central  
Figwheel: Validating the configuration found in project.clj  
Spec Warning:  missing an :output-to option - you probably will want this ...  
Figwheel: Configuration Valid ;)  
Figwheel: Starting server at  
Figwheel: Watching build - dev  
Figwheel: Cleaning build - dev  
Figwheel: Starting server at  
Figwheel: Watching build - dev  
Compiling "main.js" from ["src/cljs"]...  
Successfully compiled "main.js" in 1.95 seconds.  
Launching ClojureScript REPL for build: dev  
Figwheel Controls:  
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild [id ...])      ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once [id ...])           ;; builds source one time
          (clean-builds [id ..])          ;; deletes compiled cljs target files
          (print-config [id ...])         ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
          (figwheel.client/set-autoload false)    ;; will turn autoloading off
          (figwheel.client/set-repl-pprint false) ;; will turn pretty printing off
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: Control+C or :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application  

Load up a browser tab pointed to http://localhost:3000 and you should see, in your browser, the message "Hello World".

Hello World

Some React magic

ClojureScript web development is heavily focussed on the React ecosystem.

Several React bindings exist; one of the easiest to get started with is Rum.

Add the following to the :dependencies key in your project.clj:

[cljs-ajax "0.7.3"]
[rum "0.11.2"]

Modify core.cljs so it includes the require for Rum and a component definition (which creates a React component under the hood), then mounts it on the document body.

(ns connect-example.core
  (:require [rum.core :as rum :refer [defc mount]))

(defc label [text]
  [:div {:class "label"} text])

(mount (label "Hello again") js/document.body)

Restart Figwheel (Ctrl-C in your terminal then lein figwheel again) and refresh your browser, and that's it - you're seeing a React rendered application!

A simple list-based UI

Now we have the basic structure to put a React UI on screen, let's fill it in with something that's actually useful.

To match the wireframe above, we'll have an H1 for the title:

(defc heading []
  [:h1 "JIRA Activity Feed"])

And a component to iterate over a list of JIRA issues - so we'll call the display of each result an "issue". We'll create some dummy data to render initially, too.

(defc issue
  [{:keys [id summary type timestamp] :as issue}]
   [ id]
    [:div.title summary]
    [:div.detail type]
    [ timestamp]]])

(defc issues
   (map issue is)])

(def fake-issues '({:id "FOO-1" :summary "The Title" :type "jira:issue_updated" :timestamp 123456789}))

(defc content
   (if data
     (issues data)
     [:div "No activity found in feed."])])

(mount (content fake-issues) (.getElementById js/document "content"))

Save everything and reload your browser and you should see a very basic list and title.

It could probably do with some styling, so let's tweak our index.html as follows:

<!DOCTYPE html>  
    <link rel="stylesheet" href="/site.css" />
    <div id="content"></div>
    <script src="cljs/main.js" type="text/javascript"></script>

Create a file resources/public/css/site.css with some simple styles:

body {  
  font-family: sans-serif;

.container {
  margin-left: auto;
  margin-right: auto;
  padding-top: 30px;
  width: 800px;

div.issue {  
  font-size: 16px;
  width: 600px;
  border: solid black 1px;
  padding: 1em;
  margin-bottom: 1em;

.issue .id {
  font-weight: bold;
  float: left;
  width: 75px;
  height: 50px;
  text-align: center;
  padding: 1em .5em 0 0;

.issue .info {
  height: 100%;

.issue .summary {
  font-weight: bold;
  padding-bottom: 0.5em

Restart lein figwheel again and reload and everything should be perfect.

NB: once you get to this stage, Figwheel's most awesome benefit kicks in - hot auto-reload of ClojureScript and CSS files. Just save in your editor and the browser updates on its' own!

AJAX comms

Now let's get the UI to query the server side for our JIRA information...

Add a new require so the top of core.cljs looks like this:

(ns connect-example.core
  (:require [rum.core :as rum]
            [ajax.core :refer [GET]]))

and then let's add a function to fetch and display the data (and actually call that function to do the display)

Remove the existing mount form and add this to the end of core.cljs:

(defn handler [data]
  (mount (content data) (.getElementById js/document "content")))

(fetch-issues handler)

Finally, let's get the browser app to poll the server every 5 seconds to update the feed.

  #(fetch-issues handler)

What have we got?

Building a JIRA integration with Clojure & Atlassian Connect We've built a dynamically-updating ClojureScript React application that's showing live changes from JIRA.

It turns out it's surprisingly easy to integrate a Clojure app with JIRA through Atlassian Connect.

Evidently, what we've built here is only the barest beginnings of a production app; in real life you'd want to check the validity of inbound events using the JWT functionality built into the Connect API, and you'd at least want a proper database of some sort to persist your data into.

And remember - the full working source to this application is on GitHub here for your reference:

One more thing...

If you like what you see here, please check out our application, CTX, which lets you and your team search your apps like JIRA, Trello, Slack, GitHub, Google Drive, email and more in one shot. We offer a free trial with no credit card required.


Helper code to mimic Clojure fns in Scala

I’ve finished my 3.5 year stint writing Scala, and I haven’t stopped missing writing Clojure. The knowledge of Clojure continues to heighten and inform my programmer sensibilities. One thing that I appreciated about Scala is that it was as good of a medium as you might practically find to allow writing Clojure without writing Clojure. I liked to see Scala as the canvas on which I painted my Clojure ideas. Because Scala makes itself amenable to many styles of programming at once (at least, FP and OOP), it was possible to write code by imagining what the Clojure code would look like, and then writing it in Scala syntax. Interestingly, the more I did this, and the more faithfully I did so, the more people implicitly (no pun intended!) acknowledged the code as “good Scala code”. Because, you know, most Scala programmers agree that good Scala code puts “val”s at the top of a function, uses immutable collections exclusively, prefers functions over (object) methods, and makes functions small, stateless, and composable. More on that later. Here, I want to simply release some of the code that I wrote in Scala to fill in perceived gaps in Scala’s Seq abstraction, where the perception is based on what I was accustomed to using in Clojure.

Code snippets for implementing Clojure fns / functionality in Scala

Note: the following code snippets are Apache 2 licensed, so go ahead and use them wherever you would like as you see fit!

The first code snippet is perhaps the more interesting of the two. I provide my implementations in Scala of Clojure’s merge-with and partition-by:

The code started because something as simple as partition-by didn’t exist in Scala, and there was really no way to cleanly finish the task I was working on without going off and implementing it. Soon after, merge-with followed, and the “mergeLeftWith” was created to offer a version that starts with an initial value. The analogy is if merge-with is like reduce with no initial value argument, then “mergeLeftWith” is like using Clojure’s reduce with an initial value argument (aka Scala’s foldLeft).

The second code snippet was useful for reducing all the boilerplate that inevitably surrounds the use of Options in Scala. I also added some pretty-printing functions that I used in testing:

On the topic of whether all the ceremonial code required for Scala’s liberal use of Option (now partially present in Java 8+ due to Java’s careful embrace of FP), you should really see Effective Programs – 10 Years of Clojure by Rich Hickey. It articulates well the inherent tradeoffs that we make in our choice of programming languages, which are merely tools to a means. But it brilliantly articulates an opinion/perspective that is practical and speaks to my sensibilities of why I found the boilerplate code in Scala slowing me down more than I would like for the amount of perceived benefit I got in return (not much). Most of the benefit in terms of confidence in my code came from my various unit and integration tests.

And speaking of tests, don’t underestimate the utility of the pretty-printing functions. The reason why I created them was because I had to convert Clojure code that I wrote that used the expectations testing library. That library is amazing, especially when your logic requires data structures. The library isn’t radically different to other “fluent” testing libraries, nor is that where most of the benefit lies. The real benefit occurs when you spend the most time using it — when your tests fail! And you don’t necessarily look back at your test code, but rather, you look at the test output to gather clues of what failed and how. Expectations does the following in its error output:

  • re-prints the test code causing the failure, with the provided values plugged into the code if necessary
  • instead of printing “actual value [A] is not equal to expected value [E]“, it neatly prints (using line breaks and horizontal spacing) the values so that they line up. (I can’t tell you how many times I’ve seen the test error output that reproduces default Java object printing of 2 large, detailed objects side by side without even line breaking
  • more importantly and awesome-ly, it only shows you the portion of expected and actual values you need to see
  • and in a terminal with colors, you get different colors for the re-printing of the original test code, the expected value, and the actual value

I wasn’t about to do all that because I couldn’t possibly do so. I don’t think Scala has data structure diff’ing libraries because it doesn’t share Clojure’s proud focus on data-oriented programming. So the best I could do to recreate expectations in Scala was create helper test functions, one for comparing sets, one for comparing maps, one for comparing seqs, etc. where the differing values would be print on separate lines so that they would line up and you could more easily see where they diverge. For seqs, my testing fns would additionally iterate through the seqs and print the indices of the last congruent & first divergent elements in the seqs. Ultimately, the less time you spend sifting through the error messages, the faster your unit/integration tests are, and the faster you get back to doing the interesting, productive work that you intended to.

Parting thoughts about Scala

I found it strange that I stepped in my previous role, that used Scala heavily, by basically just writing my Scala code as if it were Clojure and not getting punished too much for it. I created a couple of semi-stateful OOP-y classes in my very first Scala program just to make it not look obvious that I was doing so, but then I got critiqued for how it made my code confusing. At a high-level, my code:

  • used Scala object classes in lieu of Clojure namespaces
  • wrote functions statelessly, with all the vals and block-local function definitions occurring before any other code in the function
  • used Scala case classes to represent Clojure heterogenous maps / Clojure records
  • avoided any typical OOP “plain” Java classes
  • used Java interfaces as substitutes for Clojure protocols

With all that, combined with my helper fns/code in main code and test code, I felt pretty comfortable in Scala. And arguably, I may have been quicker in development than many Scala programmers around me who inevitably got caught in some compiler error or type representation riddle because they felt they were trying to do Scala the “right way” (for whatever value of “right way” they imbibed).

“Hey, (at least) it’s better than Java!” That’s the most popular selling point that I’ve heard for Scala through the years. And I agree. There’s nothing more that I can say further on the topic that Rich doesn’t say much more insightfully in his his talk Effective Programs – 10 Years of Clojure. I don’t know what the future holds for programming languages. But I’m still optimistic in declaring that Clojure is a language for the ages.


De-throning the List

Listen, I have a plan. What kind of a plan, you ask? A cunning one. As cunning as a fox? Yeah, I'd say so. As cunning as a cat that has trained a man to bring her food at the ring of a bell? Yeah... no. I mean, that is pretty clever. Let's instead say that I have an idea which could potentially be a very good one:

I want to remove the List.

Hear me out

Every programming language has a general purpose collection type. Most non-functional languages, like Javascript and Java, uses an array. However, in functional languages it's not too uncommon to see a list. Why is that?

For the most part I suspect the reason is historical. Efficient immutable arrays is a pretty recent invention, first being seen in Clojure back in 2007. Popular functional languages like Haskell, OCaml and Common Lisp are from the 80's and 90's, so it simply wasn't available at the time.

In addition, the list is very simple to implement and get right. And it does have some strong suites. For instance, if you want to access the first element, remove it or even add a new first element, you can do so in constant time. You can also iterate over the collection, from left to right, in linear time, as you'd expect.

But it also has some shortcomings which can be hard to swallow. If you want to retrieve or modify any element but the first, you'll do so in linear time (yuck). If you want to iterate the items from right to left, which you have to do when performing map or filter, you'll have to reverse the list first (yes, really!).

Another thing to mention is that List isn't really suited for a browser environment. Let's take a look at how [1, 2] is compiled down to JS:

{ ctor: '::', a: 1, b: { ctor: '::', a: 2, b: { ctor: '[]' } } }

There's a problem with this though. Once the list gets big enough, browsers (or at least Chrome) will crash due to stack overflow exception. So in Elm 0.19, this is what the same list compiles to:

<core_namespace>$ToList([1, 2])

This is much shorter and will minify beautifully, but also means that every list literal in your application has an extra cost at runtime. An inlined Array wouldn't have these issues.

Because of the issues I've just mentioned, I believe List isn't fit to serve as the default collection type in Elm. I think Array would serve this role better, especially considering it works more like what people are used to from Javascript.

So we'll replace List with Array, problem solved!

Well, not so fast.

First off, the implementation details of Array are hidden inside an opaque type. This is done with good reason, as Array is a tricky data structure to get right. But it also means that there are certain things you cannot do efficiently with an Array, which are trivial to do with a List, like filtering elements until you've found a certain amount.

The Array also seems to be bad at all the things a List is good at. For instance, if you wish to insert or remove an element at the beginning of a List it's a constant time operation. Doing the same with an Array requires re-building the entire data structure, which in comparison is horribly slow.

Finally, there are certain operations that are implemented for List but not for Array, like sort.

So we're stuck with the List then?

That's the thing. I don't think we are. I believe all the problems I've mentioned can be solved, and that's what my cunning plan is all about.

Over the next couple of weeks I'll explain my plan in full. There will be benchmarks, code snippets and maybe even another reference to Black Adder. The plan, as sinister as it is, does require a lot of work however, so I wouldn't count on seeing it implemented any time soon.

Tune in next time to see how we can make Array more flexible.


Clojure/DevOps Engineer at Red Pineapple Media (Full-time)

Want to work with Clojure in Berlin?

Red Pineapple Media distributes video advertising to more than 50 markets, and we've been expanding consistently since we started 6 years ago.

Our engineering team is built around a strong culture of teamwork. Code reviews, joint presentations and group decisions are standard for us. We'll often pick up the projector and do pair programming on the big wall of our office, or just sit together in one of our quiet rooms to prepare architectural changes or to give peer-to-peer feedback.

We care deeply about continual improvement, and have an RFC process in place to enable team members to influence our evolving standard and practices.

We’re in an exciting phase right now as we’re transitioning to programmatic buying and selling of advertisement space. The number of non-trivial challenges ahead is large, and the learning opportunities rich. From intelligently bidding for advertisement space in real time, to applying clustering and classification techniques to improve ad targeting, our future is full of opportunities for functional-programming goodness 

What attitude are we looking for?

  • You have a strong sense of ownership and responsibility;
  • You’re ready to work independently, clearly define problems, and objectively evaluate tradeoffs of potential solutions;
  • You’re naturally motivated and proud of doing great work;
  • You believe that clear communication (including documentation and Git history) are a key part of good software engineering;
  • Your thought process is guided by rationalism and evidence rather than gut feeling or tradition;
  • You enjoy diversity (including differences of opinion), and have a humble attitude when engaging in discussions;
  • You sincerely welcome constructive criticism, and communicate with regard for the feelings of others.

What do we value in people?

  • Code needs to be read by humans, so you write code and documentation optimising for clarity;
  • You understand the importance of testing and automation, and avoiding repeating the same mistakes;
  • You get things done not only when starting exciting new projects but also when refactoring inelegant legacy code;
  • You're ready to work daily with Linux systems, AWS, Git, Docker and a steadily growing Microservice infrastructure;
  • You’re ready to embrace the DevOps way of life, because an Engineer’s work doesn’t end after ‘git push’;
  • Your studies were in computer science, mathematics, engineering or a related area;
  • Alternatively: you have an awesome GitHub profile to show off :-)
  • You have worked with Clojure, ClojureScript, or other functional programming languages (and you're interested in learning Clojure!);
  • You have worked with distributed systems and have dealt with problems related to concurrency, performance and fault tolerance;
  • Curiosity is a constant in you. You don't settle for the same language or paradigm for too long and are eager to experiment and learn new things.

What we offer

Compensation of €42,000 - €60,000.

Never a dull moment. Besides writing code, we also do our own DevOps, testing, retrospectives, planning, architecture, peer-to-peer evaluation, coffee and cooking.

Small teams, lots of freedom

Every few months we organise in small teams around well-defined business objectives, and we have complete ownership over the approach and process used to achieve them. This includes: planning, work methodology, design, development and maintenance. We also decide when and how to tackle technical debt, and we are the sole owners of these decisions. Then, after some time, we reset the teams and choose new challenges to work on.

A safe haven for functional programmers

Even if your background is in non-functional programming, if you want to do functional programming then Red Pineapple Media is the place for you. You’ll find as many challenges here as learning opportunities. You’ll also have the chance to try new things and explore. We regularly hold research weeks where you can spend your time investigating and building anything you want.

Friendly environment plus quiet rooms

We regularly hold team breakfasts, barbecues, and ping-pong matches. We’re super animal friendly: 4 dog owners, 2 vegans, 1 vegetarian and a cat-loving CEO. Naturally, dogs are welcome in the office. But we also optimise for focus and have quiet rooms where you can do power-naps, meditate or just work in isolation.

Do what you want - and own it

We believe that when given independence and room to grow, people tend to do great things. If you want to learn something new, fix that annoying Elasticsearch performance problem or focus on creating nicer looking brandings or reports, it’s up to you. As long as our business objectives are achieved, you own and control your time and what to do with it.

About Red Pineapple Media

Red Pineapple Media is a passionate, dedicated team of content and marketing professionals connecting advertisers, publishers, and consumers all over the globe. We’re driven by one simple goal: deliver campaigns that mutually benefit the advertiser, publisher, and the consumer. It’s a strategy that has seen us successfully help over 300 advertisers increase their exposure and boost online media ROI in more than 56 markets – and counting.

You can learn more about us at

Get information on how to apply for this position.


Testing Clojure Web Applications with Selenium

This article is brought with ❤ to you by Semaphore.

Selenium is a commonly used set of tools for automating browsers. It allows you to drive a browser interaction with a web page by writing code. It's most often used to write browser-based tests for web applications. Tests can be executed in a development environment or even on a real application as part of a smoke test suite.

Selenium provides support for most popular languages and browsers. This tutorial explains how to use Selenium for testing Clojure web applications. Our setup will be based on clj-webdriver and Firefox, and we will use Compojure to write a very simple web application.

Setting Up the Project


For developing a Clojure application using this tutorial you will need:

  • Java JDK version 6 or later.
  • Leiningen 2.
  • Firefox 39 or lower.

At the time of this writing, if you have a newer version of Firefox, the example might not work for you. If it doesn't, you will need to downgrade Firefox. You can see what's the last version of Firefox that Selenium officially supports on the Selenium changelog page. If you plan to use Selenium-based tests regularly, you might want to hold Firefox updates until Selenium starts supporting them.

Create a Hello World Compojure Application

Compojure is a routing library for Ring, and a popular choice for writing web applications in Clojure. Leiningen provides a Compojure template that allows us to get started with Compojure quickly.

Create a Compojure-based Clojure project:

lein new compojure clj-webdriver-tutorial

The second parameter compojure is the name of the template that's going to be used for creating the application. The last parameter, clj-webdriver-tutorial, is the name of your project.

Navigate to the project directory:

cd clj-webdriver-tutorial

Start the server:

lein ring server-headless

After the server starts, visit http://localhost:3000 in a browser and you should see the Hello World greeting from the application:

Hello World

Compojure Application Structure

The structure of your application should look like this:

├── project.clj
├── resources
│   └── public
├── src
│   └── clj_webdriver_tutorial
│       └── handler.clj
├── target
│   ├── ...
└── test
    └── clj_webdriver_tutorial
        └── handler_test.clj

The file that we're interested in is src/clj_webdriver_tutorial/handler.clj. If you open it, it should contain the following code:

(ns clj-webdriver-tutorial.handler
  (:require [compojure.core :refer :all]
            [compojure.route :as route]
            [ring.middleware.defaults :refer [wrap-defaults site-defaults]]))

(defroutes app-routes
  (GET "/" [] "Hello World")
  (route/not-found "Not Found"))

(def app
  (wrap-defaults app-routes site-defaults))

It defines the access point to the application (/ - the root path), and we can see that this is where that "Hello World" is coming from.

We can also notice that Leiningen created the handler_test.clj file that's using clojure.test to test the handler. Since we're concentrating on clj-webdriver instead, let's remove the test:

rm test/clj_webdriver_tutorial/handler_test.clj

Install clj-webdriver

Install clj-webdriver by adding the project development dependencies to project.clj:

(defproject clj-webdriver-tutorial "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url ""
  :min-lein-version "2.0.0"
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [compojure "1.3.1"]
                 [ring/ring-defaults "0.1.2"]]
  :plugins [[lein-ring "0.8.13"]]
  :ring {:handler clj-webdriver-tutorial.handler/app}
  {:dev {:dependencies [[clj-webdriver "0.7.1"]
                        [org.seleniumhq.selenium/selenium-server "2.47.0"]
                        [javax.servlet/servlet-api "2.5"]
                        [ring-mock "0.1.5"]
                        [ring/ring-jetty-adapter "1.4.0"]]}})

There are several new things in project.clj:

  • We added clj-webdriver "0.7.1".
  • Next, we explicitly added the selenium-server that supports at least Firefox
    1. If you have a newer version of Firefox, you can try upgrading selenium-server to the latest available Selenium version.
  • We also added ring-jetty-adapter to run the application before executing tests.

First clj-webdriver Test

Create the features directory where you will put clj-webdriver tests:

mkdir test/clj_webdriver_tutorial/features

Open test/clj_webdriver_tutorial/features/config.clj and add some common configurations that we will use in tests:

(ns clj-webdriver-tutorial.features.config)

(def test-port 5744)
(def test-host "localhost")
(def test-base-url (str "http://" test-host ":" test-port "/"))

The default configuration states that tests will be executed against the application running on http://localhost:5744. 5744 is the default port for Selenium.

Our first test will check if the home page really displays the "Hello World" message. Since we're testing by opening a real browser, the test needs some setup and teardown. Here are the steps that need to be executed:

  1. Start the server for the application.
  2. Open the root path in the browser.
  3. Check if the "Hello World" message is present on the page.
  4. Close the browser.
  5. Shut down the server.

Let's write a skeleton of that code in test/clj_webdriver_tutorial/features/homepage.clj:

(ns clj-webdriver-tutorial.features.homepage
  (:require [clojure.test :refer :all]
            [ring.adapter.jetty :refer [run-jetty]]
            [ :refer :all]
            [clj-webdriver-tutorial.features.config :refer :all]
            [clj-webdriver-tutorial.handler :refer [app-routes]]))

(deftest homepage-greeting
  (let [server (start-server)]
    (to test-base-url)
    (is (= (text "body") "Hello World"))
    (stop-server server)))

The most important parts are the to and text functions that are used for navigating to a page and extracting text from a node, respectively. They are part of the clj-webdriver Taxi API.

Before running the test, we need to implement the start-server, start-browser, stop-browser and stop-server functions.

The start-server function is the most complex one, as it starts the jetty server on the test port and waits for the server to be started:

(defn start-server []
  (loop [server (run-jetty app-routes {:port test-port, :join? false})]
    (if (.isStarted server)
      (recur server))))

The other functions are much simpler:

(defn stop-server [server]
  (.stop server))

(defn start-browser []
  (set-driver! {:browser :firefox}))

(defn stop-browser []

As they are actually wrappers against respective functions in the clj-webdriver, they can be used directly in a real application test.

Putting in all together and our first code in test/clj_webdriver_tutorial/features/homepage.clj looks like this:

(ns clj-webdriver-tutorial.features.homepage
  (:require [clojure.test :refer :all]
            [ring.adapter.jetty :refer [run-jetty]]
            [ :refer :all]
            [clj-webdriver-tutorial.features.config :refer :all]
            [clj-webdriver-tutorial.handler :refer [app-routes]]))

(defn start-server []
  (loop [server (run-jetty app-routes {:port test-port, :join? false})]
    (if (.isStarted server)
      (recur server))))

(defn stop-server [server]
  (.stop server))

(defn start-browser []
  (set-driver! {:browser :firefox}))

(defn stop-browser []

(deftest homepage-greeting
  (let [server (start-server)]
    (to test-base-url)
    (is (= (text "body") "Hello World"))
    (stop-server server)))

Run the tests suite:

lein test

And you will see that the test passed:

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

You now have a basic setup for testing Clojure web applications with Selenium.

Cleaning Up

This setup works well, but we have to remember to start the server and the browser before each test, and to shut them down after the tests are done. To make things easier, we can implement test fixtures that will do this automatically before and after every test.

The fixture for handling the server can be implemented as follows:

(defn with-server [t]
  (let [server (start-server)]
    (stop-server server)))

The t parameter stands for test case function. It starts the server before the test case, executes the test function, and stops the server.

The fixture for handling the browser is similar:

(defn with-browser [t]

Using fixtures, we can write a much cleaner test:

(ns clj-webdriver-tutorial.features.homepage
  (:require [clojure.test :refer :all]
            [ring.adapter.jetty :refer [run-jetty]]
            [ :refer :all]
            [clj-webdriver-tutorial.features.config :refer :all]
            [clj-webdriver-tutorial.handler :refer [app-routes]]))

;; Fixtures

(defn start-server []
  (loop [server (run-jetty app-routes {:port test-port, :join? false})]
    (if (.isStarted server)
      (recur server))))

(defn stop-server [server]
  (.stop server))

(defn with-server [t]
  (let [server (start-server)]
    (stop-server server)))

(defn start-browser []
  (set-driver! {:browser :firefox}))

(defn stop-browser []

(defn with-browser [t]

(use-fixtures :once with-server with-browser)

;; Tests

(deftest homepage-greeting
  (to test-base-url)
  (is (= (text "body") "Hello World")))

Note that we passed the :once parameter to the use-fixtures function. This means that the browser will be started once before all tests, and stopped after all tests are finished. The same goes for the server. This should significantly speed up the tests in the file.

In a real application, you can move fixture functions to a separate namespace that is shared by all tests.


Selenium is a valuable tool for testing web applications, and it is indispensable if an application is heavily using JavaScript. Setting up Selenium with Clojure requires several steps covered in the tutorial. Using fixtures, a test setup can be reused, which results in cleaner and faster tests.

You can find more information about common functions for interacting with web pages and inspecting page content in the clj-webdriver Taxi API documentation.

This article is brought with ❤ to you by Semaphore.


How to serve ClojureScript files in development

When I develop ClojureScript projects, I almost always use Figwheel. It’s a great tool, but sometimes my app ended up using stale files. This led to some very confusing debugging sessions. It only happened some of the time, and was always fixed after a hard refresh. I thought about just disabling the browser cache, but I didn’t like ignoring the issue. After seeing colleagues struggle with stale caching too, I decided to figure out what was going on, and fix it once and for all.

Cache-Control rules everything around me

The first thing to do was to add a Cache-Control: no-cache header to all static file responses. Despite the name, no-cache tells the browser it can cache files, but must always validate them with the server before using them. If the browser’s cached version is up-to-date, a compliant HTTP server should return a 304 Not Modified response, otherwise it serves the new file.

If you don’t provide a caching header to an HTTP response, the browser can choose its own caching behaviour. The browser’s caching heuristics are much more aggressive than you want in development, and lead to the weird caching behaviour I was seeing.

I thought this had fixed the issue, but occasionally I would still notice stale files were being used. After looking closely at the compiled output files, I made a surprising discovery.

ClojureScript copies file modification times

ClojureScript (as of March 2018) copies the last-modified date of ClojureScript source files to the compiled JavaScript target files. This is so that the compiler can detect changes to source files. JavaScript from the Closure compiler (e.g. goog.base), gets a modification time that matches the time it was compiled.

Neither of these dates are particularly useful to use as a Last-Modified date header for caching purposes.

  • Closure compiled JavaScript doesn’t change from run to run, so caching based on last modified date will not achieve as high a hit-rate as possible.
  • ClojureScript files that use macros from other Clojure files will copy the ClojureScript files modification date to the compiled JavaScript, even if the macro files have changed and are newer. This was leading to the second round of caching issues that I saw.


To knock both problems on the head once and for all (hopefully), I added a CRC32 checksum based ETag for static file responses. I packaged this up in a library ring-etag-middleware so that other projects could also use it. I also removed the Last-Modified header from the Ring response, as it would bust the browser’s cache unnecessarily.


As best as I can tell, this has completely solved all of the odd caching issues that I was seeing, while still keeping the app snappy to load by reusing as much of the cache as possible. If you are serving ClojureScript files in development and not using Figwheel, I recommend you follow these three steps:

  1. Set a Cache-Control: no-cache header
  2. Add an ETag to your static file responses
  3. Remove the Last-Modified header


Thought Experiment: Namespaced record fields

I don’t like to nest records in Elm. It’s not that big of a deal, but it always seem to be lead to more noise than initially thought. Part of this is that Elm doesn’t have syntax that’s convenient for nested updates. Take a look at this record definition:

type alias Person =
  { name : Int
  , age : Int
  , pet : Pet

type alias Pet =
  { name : String
  , age : Int

If I had a person record and wanted to rename the pet, the code would look like something like this:

-- Can't do this
{ | name = "Fido" }

-- Or this
{ person | pet = { | name = "Fido" } }

-- It has to be this
  pet =
{ person | pet = { pet | name = "Fido" } }

As I said previously, this isn't a big deal but it does leave me with an itch. One way to allieviate this is by avoiding nested records all together:

type alias PersonWithPet =
  { personName : String
  , personAge : Int
  , petName : String
  , petAge : Int

We can make this scale by using extensible record syntax:

type alias Pet a =
  { a |
    petName : String
  , petAge : Int

{- Works with PersonWithPet -}
renamePet : String -> Pet a -> Pet a
renamePet name pet =
  { pet | petName = name }

So, this actually solves the problem but does require me to prefix all fields in the record. What if there was support in the compiler for making this nicer?

Let's switch gears a little bit and talk about my previous language-of-choice, Clojure. Clojure is a Lisp and is dynamically typed. Instead of Records one simply uses maps (in Elm we call it Dict) to group together data. Clojure has its own type to serve as keys in a map, called keywords. They look like this:

:name ;; keyword

;; Person with a Pet
(def person
  { :name "Robin"
    :age 29
    :pet { :name "Fido"
           :age 4 } } )

In Clojure, nested updates is pretty simple. If I wanted to rename the pet using the person definition above, I would do this:

(assoc-in person [:pet :name] "Baldur")

However, sometimes it makes perfect sense to avoid nesting and Clojure has wonderful support for that:

(def person-with-pet
  { :person/name "Robin"
    :person/age 29
    :pet/name "Fido"
    :pet/age 4 } )

But this still requires us prefix everything. This is where namespaced keywords comes into play:

(ns person) ;; namespace is set to person

;; This equals our previous definition
(def person-with-pet
  { ::name "Robin" ;; notice the double colon
    ::age 29
    :pet/name "Fido"
    :pet/age 4 } )

;; This is also the same thing
(def person-with-pet
  #:person{ :name "Robin"
            :age 29
            :pet/name "Fido"
            :pet/age 4 } )

The double colon in the example above will fill in the current namespace as the prefix of the keyword. What could this potentially look like in Elm?

module Pet exposing (Pet)

type alias Pet a =
  { a |
    :name : String ;; expands to pet/name
    :age : Int ;; expands to pet/age

module Person

import Pet as P

type alias PersonWithPet =
  { :name : String -- person/name
  , :age : Int -- person/age
  , :P/name : String -- pet/name
  , :P/age : Int -- pet/age

{- Works in PersonWithPet -}
renamePet : String -> P.Pet a -> P.Pet a
renamePet name pet =
  { pet | :P/name = name }

Is this an improvement? Maybe. It might be better to instead find a good syntax for nested updates, but I wouldn't mind just having a simple syntax to work with flat records.


Langohr 5.0.0 is released


Langohr is a small Clojure RabbitMQ client.

5.0.0 is a release that upgrades Java client dependency to 5.x..

Changes in 5.0.0

RabbitMQ Java Client Upgrade

RabbitMQ Java client dependency has been updated to 5.x.

JDK 8 is Now Required

RabbitMQ Java client 5.x requires JDK 8. It’s a good chance to drop support for older JDKs in Langohr. Langohr 4.x continues to use a JDK 6 and 7-compatible version of the Java client.

Queueing/Blocking Consumers are Removed

RabbitMQ Java client 5.0 removed a long deprecated queueing consumer abstraction that used an internal j.u.c queue for deliveries and acted as an iterator. That consumer implementation never supported automatic connection recovery and isn’t necessary with modern consumer operation dispatch pool.

Langohr follows suit and removes the following functions based on the QueueingConsumer:

  • langohr.basic/blocking-subscribe
  • langohr.consumers/create-queueing
  • langohr.consumers/deliveries-seq

langohr.consumers/deliveries-seq may be reintroduced in the future if a reasonable imlementation for it comes to mind/is contributed.

clj-http Upgrade

clj-http dependency has been updated to 3.8.x.

Change Log

Langohr change log is available on GitHub.

Langohr is a ClojureWerkz Project

Langohr is part of the group of libraries known as ClojureWerkz, together with

  • Elastisch, a minimalistic well documented Clojure client for ElasticSearch
  • Cassaforte, a Clojure Cassandra client built around CQL 3.0
  • Monger, a Clojure MongoDB client for a more civilized age
  • Neocons, a client for the Neo4J REST API
  • Quartzite, a powerful scheduling library

and several others. If you like Langohr, you may also like our other projects.

Let us know what you think on Twitter or on the Clojure mailing list.

About The Author

Michael on behalf of the ClojureWerkz Team




Clojure survey, macrology, Elements of Clojure
View this email in your browser



Libraries & Books.

  • Zach Tellman has finished a complete draft of his book Elements of Clojure. He's also doing an AMA on ClojureVerse next week. Chas Emerick (among many others) has high praise for it:
    "This is a great book that uses Clojure as the medium to explore some of the essential questions in computing, and maybe some ways to find an answer or two. 
    Zach has one of the sharpest minds I've ever encountered. You'd do well to soak this up."
  • I'm a bit late on this, but Sean Corfield has taken over maintenance of core.cache and core.memoize and both have new releases out.
  • Kapok is a new lisp on the Erlang VM
  • Piggieback is seeing some new activity from Bruce Hauman and Bozhidar Batsov
  • Confuse is a library for machine learning classification metrics

People are worried about Types. ?

Recent Developments.



Copyright © 2018 Daniel Compton, All rights reserved.

Want to change how you receive these emails?
You can update your preferences or unsubscribe from this list

Email Marketing Powered by MailChimp


Copyright © 2009, Planet Clojure. No rights reserved.
Planet Clojure is maintained by Baishamapayan Ghose.
Clojure and the Clojure logo are Copyright © 2008-2009, Rich Hickey.
Theme by Brajeshwar.