This post is part of our Clojure In series, taking an in-depth look at companies across Europe who have adopted Clojure. In this article we look at Social Superstore, a London start-up providing a social platform for reselling products.
Clojure has helped us to focus on delivering business value
Social Superstore allows people to setup their own 'store' to review and promote existing products to their social networks. Entrepreneurs are given a helping hand to resell without having to worry about managing stock, logistics and advertising. Celebrities also use it to make product recommendations to their following. The platform has worked well with fashion bloggers who can link subscribers to their own store of selected products.
The tech stack is predominantly Clojure based. They have seven Clojure developers and four developers working on native applications.
Malcolm Sparks and I paid a visit to their office in Farringdon, London for a chat.
Migrating from Angular.js to ClojureScript
Jon: So what's the tech story here?
James: There has always been Clojure and AWS since a consultancy bootstrapped the MVP. It originally had a Clojure backend and an Angular front end. About 6 months in we started a ClojureScript prototype and we decommissioned the Angular app.
Jon: How long did it take you to move from Angular? What was the motivation?
James: It took about 2-3 months to decommission Angular. The code-base was getting harder to maintain; features coming in were taking longer to develop and the results were buggy. We also wanted the benefits of using ClojureScript; the state management you get with ClojureScript and React.js - with frameworks such as Om and Reagent. We were also a Clojure team.
Jon: You were able to go 'full stack'?
James: Yes, all the devs can work on both the front and back end.
Dave: One of the benefits is that we can put logic in
cljc and share code across layers.
Jon: Looking back, do you think the mature ClojureScript solution is better than what you had with Angular?
James: Yes, with Angular there was lots of magic involved. ClojureScript very much suited our team, although the second generation of the front-end was always going to be better.
Jon: What is your non-Clojure stack?
James: PostgreSQL, AWS, SQS, Kubernetes, and Docker.
Jon: How do you provision to AWS?
Dave: We use CloudFormation and Chef; they meet in the middle.
Jon: What's your opinion of Kubernetes?
Dave: Kubernetes is great. It was a bit fiddly initially but that's improved a lot. We get smooth, zero downtime deployment. You can throw containers at it, and it becomes super-easy to try out a new service. Upgrading versions of Kubernetes itself has been fine.
Jon: Have you looked at the AWS equvialent of ECS (Elastic Container Services)?
Dave: ECS, certainly at the time, wasn't as feature rich. Kubernetes also gives you a bit more cloud portability; you can port your applications to the Google Container Engine.
Jon: What's in your Clojure application stack?
James: We're fairly vanilla Clojure. We use Ring with a general set of custom middleware, Bidi for routing, Medley for a whole host of utility functions, Nomad for configuration. We are using Bounce for lifecycled components.
Jon: Bounce is what you wrote as an alternative to Component? How does it differ?
James: Minor things - I wouldn't push Bounce heavily as it has 'grown' into our application. I wanted to avoid having a partially started system in the REPL which is difficult to recover from. I also personally prefer using vanilla values and functions as opposed to protocols. Mount is more similar to Bounce but I'm not keen on the top-level state; Bounce pulls ideas from both Component and Mount.
Jon: What else are you using?
Dave: I like Yesql for SQL usage; it has a good level of abstraction.
James: We don't have a traditional REST API; we've moved to a command/query API - taking inspiration from Bobby Calderwood's talk at the Conj. We compose API requests as Clojure data structures and the front-end requests just the data it needs as part of 'pull' model. We developed a library for this but it's currently in house, we stole ideas from GraphQL.
Jon: Have you used Om Next?
James: We tried it on some hack nights but we're very much tied to Reagent right now.
Jon: How have you found Reagent?
James: Generally, it's very good. I would say that there are a few pitfalls that we've learned to avoid, for example where you can potentially close over the wrong variable leading to components remounting/rerendering too often, or not often enough. To get around this we've taken some inspiration from Elm; we keep state at the top in a separate tree and use stateless views with events firing out from those views.
We have a giant multi-method that works off incoming events, which is a lot like Elm.
Dave: Elm is strong with types. Without types it can get confusing - the state tree, the view tree, and events flying about. Having some lightweight assistance on keeping track of the types is helpful; ensuring the trees and data types are correlated.
James: We've stolen a lot of ideas from Elm.
Dave: We also use Devcards, it's really good for prototyping.
The Types Debate
Jon: Where do you guys stand on the types debate?
James: Clojure has a lot of benefits of being dynamically typed; it's second to none for playing with data and prototyping.
A challenge we have is handling data coming from a variety of disparate sources, where the data quality can be poor. Originally having a dynamic structure was useful, but now that the system has matured, we could benefit from more rigorous typing. We've gone for Schema, not so much Spec because it wasn't around.
Dave: By not having types you're taking on tech debt in a way, which is good when you're finding your feet. Once code solidifies you've then got work to add rigour to it.
James: - Our typing is particularly focused on system boundaries - our APIs and data coming from external sources.
As a hobbyist Haskeller I miss ADTs from Clojure.
Dave: Always the grass is greener.
James: Without a shadow of a doubt.
Jon: How have you found hiring?
Dave: Everyone on the team applied because it was a Clojure job.
James: We haven't had as many people to interview, but we've had a better hit rate of success. Overall Clojure has been very good.
Jon: How about training?
James: Learning the basics of Clojure is great - it's a really simple language. In my experience, though, people can find it quite difficult making the jump from a hobbyist to writing serious Clojure in production. In OOP, the larger architectural patterns are well-documented; in FP, it's quite a different mindset.
One thing that helps is watching tech talks as a group and going to conferences.
Jon: What's the size of your Clojure code-base?
James: About 25K LOC Clojure, 30K ClojureScript.
Jon: Have you learnt any lessons about refactoring on a mature Clojure code-base?
James: The biggest thing is using schema on the system boundaries. In terms of refactoring, the REPL is very handy - we have lots of comment blocks with examples on how to use the code and example data structures. REPL driven development is a big win for Clojure; we iterate quite fast.
Jon: Do you have much automated testing?
Dave: We do have smoke tests that catch quite a lot of things. We also have a lot of monitoring using Kibana and LogStash.
James: A principal we follow is to reduce magic where possible. When you make a change you want to be confident about the impact of that change. Here schemas help us to ensure that impact of changes are well known and small.
We've also split out IO from non-IO code. Before, lots of code pulled out data from the DB and munged it etc. Now that we've split it out, the amount of IO code has become smaller; reducing the surface area for things to go wrong.
State of Clojure
Jon: Your view on the state of Clojure?
James: Clojure has been very stable since I came here in January 2015. We have a good handle on how to architect Clojure code; and for data transformations it's particularly well suited.
ClojureScript, less so. Having a ClojureScript project a couple of years old you can now see the 'lava layers'; the record of everything that was best practice at the time. It's still changing quickly.
The decision to go on the JVM has benefited us hugely. The industry has 20-30 years of knowing how to deploy Java applications.
Dave: Things like New Relic working straight away has been a plus.
Jon: What do you think the state of Clojure is in London?
James: London seems to be a hub for Clojure. There are lot of community events: dojos, Clojure Bridge, multiple conferences.
Clojure itself has stablised, now coming up to 10 years old. It's not as 'new' as in the early days.
Jon: Any last words?
Dave: Thank heavens we're not using Java.
James: Clojure has helped us to focus on delivering business value, to help us get to the core of our business requirements.
Sign up to the JUXT newsletter