Should I be using a Modern do-it-all frameworks for Clojure and Clojurescript? My opinion is of course colored by my years spent in JAVA/J2EE's world of web frameworks ( Wicket, Tapestry, JSF + Hibernate EJBs etc.). In this world, I found most of my time was spent in learning the deep intricacies of the framework and fitting it to my use case. I began to hate every minute I spent on these frameworks.
And then I discovered Clojure and Ring and later Liberator. What I like about my current setup is that I have full control over my framework and my team knows its ins and outs and the corresponding Clojure code is succinct enough to allow developers to come upto speed quickly. Our set up is not sexy but it works and we know how exactly it works.
I am not sure what I would miss if I did not use a framework like this. Is Liberator no longer good enough ?
I feel the same way. I hadn't heard of Liberator, though. We're using Reitit for routing both in backend and frontend and middleware for content negotiation and such. It's an awesome setup (though there were some rough edges starting out), almost everything is easy to understand just by reading the source.
Just today I was thinking how nice it is to have the backend route tree available in both backend and frontend code, so e.g. it's easy to tell in advance in cljs whether the user has permission to use a specific endpoint. The route tree includes authorized roles and I wrote a simple auth middleware to enforce them.
My take is that frameworks are great when you are building new projects, but become somewhat limiting over time. Also, with a complex application you often find that not everything can be nicely abstracted and there are features which cut across the stack, requiring changes everywhere.
In my fairly large Clojure+ClojureScript app I do not use a framework, mostly because nothing existed when I started. I pick libraries and make them work together. This has advantages, but also brings endless frustration when I have to deal with stuff like web authentication, file uploads, or Oauth2. I really wish there were good solutions for these kinds of generic problems.
Also, not all choices made by the framework are necessarily a good fit for every application size. I used to like Reagent, and I still think it's great for learning. But then it became limiting, I switched to Rum and never looked back.
But in general, why not take the Clojure approach and use the right tool for the right thing? You don't have to chain yourself for life to a framework, you can use a framework for one app and use a bunch of separate libraries in another. I think in the Java world there is so much incidental complexity, that you have to choose a single framework and stick with it, as it's likely the only thing you will be able to learn. Not so in the Clojure world.
The huge reason I'm not using Clojure for webdev is because there is rarely an accurate, current guide that demonstrates how to plug things together to get a typical CRUD+user auth website going.
I know that everything I need already exists, but I have yet to find a complete tutorial (Rails Depot, for example) that actually works. There's always some gap that isn't explained or some step that fails because the guide is 2+ years old.
So while I do agree that choosing your own libraries (and essentially building your own custom framework, because that's what they all end up as) is preferable long term, it is incredibly frustrating trying to build that first webapp.
I’m a fan of using tools that fit your problem. I think Fulcro is probably better if you have a frontend that uses Fulcro/React. It can bring order to the sometimes Wild West of data fetching that happens in the browser world. That said - if your problem isn’t a full stack web app, don’t use Fulcro. Like you said, there is already very good tech for doing REST APIs or static html pages or the like. I don’t think Clojure has any frameworks like Ruby on Rails where you use it for all use cases. For example - I’d probably switch to httpkit over ring if I needed to solve a problem with async websockets.
Just a note for anyone affiliated with Fulcro who reads this:
I opened the links as new tabs on iOS Chrome to check them out, and it turns out they don’t go to the anchors unless I switch to the tab in a short enough time. Not sure if Chrome or the page itself (presumably using Fulcro?) is to blame here, but it was disappointing to find 3 tabs all open to the top of the page when I switched over to read them instead of the relevant sections.
Just tested in the latest Chrome -- the page does take awhile to load, and doesn't jump to the linked section until right when the page finishes loading
On Chrome iOS, a delay of around 10s before switching to the tab results in it staying at the top of the page, even after waiting around 20s. But switching quickly to the new tab loads the anchor in 1-3s.
Thanks for the links though, they gave me something intriguing to explore!
Really good work. I spent 15 minutes reading through this book over my first cup of coffee this morning.
I stopped using Clojure about 5 years ago, after two long consulting jobs using Clojure. I decided Clojure was not a perfect fit for me (and, really, neither is Common Lisp, Racket, and Haskell - more favorite languages) but I am very happy to see the Clojure ecosystem still creating such great tools around the language and functional composition.
Nothing. I use Python for deep learning but don’t care for the language. I prefer Lisp languages, most comfortable with Common Lisp but love the ecosystem around Racket. Love Haskell but I am in a many year learning curve.
I play with Pharo about once a month. Love but no real amount of time spent with it anymore. Good language and platform, very energetic community but it is a small community.
Same here. It's been on my "someday I´ll use it on a real project" list for 10-ish years. From time to time I boot an image, play around, cry a little, get back to Python.
I tend to do bottom up repl based development. For reasons that frankly I don’t remember, I compared the experience in Common Lisp to be better. And, I was always put off a little by the stack traces. I donated to Rich for the early development of Clojure, and it is a fine language and ecosystem, but it is just not a favorite language for me.
I gave Clojure another look more recently because of the bindings for the mxnet deep learning library, so I guess I didn’t completely drop it.
> All of the full stack examples use a mock server embedded in the browser to simulate the interaction, but the source that you’ll read for the application is identical to what you’d write for a real server.
God, I'm loving this already. The only other framework I know that could do this for a book is Meteor.
Would anyone recommend Clojure if you run an engineering org and get to make decisions about languages?
I've been running node shops for the last several years, and love Clojure since having hosted a couple meetups a while back.
That and being most influenced by lispy/ai culture from SICP, CTM, PAIP, AIMA books. Node is getting tiresome due to not being able to trust the ecosystem quality (which takes away some of the common justification of huge ecosystem).
Not worried about hiring since not open to fresh grads from universities or bootcamps without lots of training anyway (which can include a new lang). Are there crappy/charlatan Clojure devs out there or is it more of an elite/experienced culture?
With stuff like Fulcro, could we replace our basic REST APIs and business logic without having to re-invent too much?
In my experience -- I was a professional Clojure developer for 6 years (until 2018), and hung out in the #clojure IRC channel on freenode a lot between 2011 and 2014 -- the Clojure community tends to be very experienced. Clojure is not many people's first language, but tends to be a language you find your way to after dissatisfaction with other languages, or exposure to other lisps.
I'm sure you can find crappy or charlatan Clojure devs, but I was generally impressed with the Clojure devs I met in #clojure or at the cons. Overall I found the community to have high levels of both knowledge/experience and patience in explaining things thoroughly and precisely to people who were asking questions.
Yes. I was at Puppet (nee Puppet Labs) for four years, one of the larger Clojure shops to my knowledge (not that I've been keeping close tabs on who's picked it up recently).
I switched jobs and language choice was not my #1 criterion. A very interesting opportunity that aligned well with my values (and paid better to boot), but was in a completely different tech stack (Python DS ecosystem, with some Scala). I love the language, think it's wonderfully concise yet expressive way to think about code, runs on a great platform for the web, and is a great fit for when team size exceeds codebase size (though not so much the other way around). All my fun software projects are still in Clojure or Clojurescript; I just wrote some today to scrape doggy listings.
Ladder (ladderlife.com) is written entirely Clojure(Script). We feel it was worth it - having a single language that can stretch from frontend to ETL code is pretty powerful. The fact that parallelism in Clojure is real (and not 16 separate processes) is pretty great too. Some of the downsides are: the slow startup time and fat(ish) JavaScript payloads because of the extra runtime libraries. The community can also be a bit unwelcoming of new ideas sometimes.
If you’re thinking of migrating - especially if it’s just a rest API - just start with Ring (& maybe bidi for routing). It’s not really a framework - you can pick and choose what else fits into your stack well after that. Clojure libraries tend to be very stateless and end up composing surprisingly well. I think Fulcro is a bit more useful if you’re building a frontend from scratch with React.
In terms of hiring - it’s very similar to a niche language. You get less folks that are specialists in Clojure like people are specialists in Node or Ruby. It’s also an easy to teach the language so you can bring most people up to speed in a month or so. Our hiring process favors generalists - and Clojure fits in well for that role. I don’t think specialists would have a good time with Clojure though - it’s not the kind of language that is easy to use as a means to an end.
I work with Clojure about 50% of the time at my current job, and I can tell you that I've never felt blocked by it for anything. The libraries in Clojure are pretty ok, and when in doubt, I can virtually guarantee that a well-tested Java library exists for nearly anything you are working on.
In regards to dev-quality, I find that people that are interested in Clojure are typically good engineers, not due to the qualities of the language, but due more to its relatively niche-ness, attracting more enthusiasts.
I don't know anything about your stack, or Fulcro, but I've had no issue using Compojure for doing REST stuff.
EDIT: Just an FYI, I should point out that I work at a really big megacorporation, not a small startup or anything.
We're a very young business, iterating fast through
features, being very careful about writing maintainable
code. We have full stack Node apps and full stack Clojure
apps. This is a short thread on the difference technology
(or would it be language?) choices have made for us.
I would recommend Clojure, but not necessarily for every organization. If you have experienced, mature and well-educated developers (which means expensive developers), it's a fantastic tool. But many organizations treat developers as interchangeable cogs with mediocre skills and mediocre pay, and Clojure would not work well for them.
I also think it's very easy to tell a crappy/charlatan Clojure dev from a good one just by looking at a small sample of his code.
The main issue with using Clojure is that you are expected to build everything out yourself. A lot of dependencies tend to stop being maintained after a while. Auth systems etc. are half-baked with 10 different implementations on GitHub. The newer shinier ones lack in features while the more featureful ones are from 3 years ago and have not seen a single update since. Now lisp people like to say "but...but.. backwards compatibility!" Sure that works for a lot of things but it goes out of the window when you need to prototype something quickly and your intern using Django/Rails has already built a MVP while you are still trying to sort out your dependencies story. Now you can always pull in Java libs, but then you will spend the rest of the week writing a wrapper for a very tiny subset of features that only you would use and the next person that comes along will have to do the same thing for the subset of features that they use.
Using Clojure (other Lisps have it way worse than Clojure so they are not even worth mentioning in an enterprise context) is a bit like using an extremely new cutting edge language like Rust or Nim where there's no high quality libraries for anything except the lowest common denominator but without the massive community and support. Yes Node has its issues. But at the end of the day, you still need to get things shipped. Elegant code that would look at home in SICP is useless when money is on the line and you can't ship. Clojure et al. is very much NOT a "Move Fast and Ship" type of language. If it's for a game jam where no one's using Unity and everyone's doing OpenGL or one of those dinky little Lua game dev suites, sure go for it. If it's your company, use Go and get things into production first.
Don't get me wrong, I love Clojure. There are a lot of smart people doing interesting things with it. It's probably the most advanced of all Lisps in terms of beginner-friendly tooling alone (shoutout to the Nightcode and Parinfer authors!) and it's Java interop is tremendously powerful in the right domain. Just that the ecosystem feels at times like Android 1.5 with its terrible fragmentation problems. The main corporate sponsor behind the language gives off apathetic vibes to community needs and the lack of featureful, maintained libraries is covered up under the guise of "Big frameworks bad! Elite programmers build their own!"
That's precisely the problem that Luminus [1] solves. It provides a batteries included template that follows best practices as well as documentation on how you would accomplish many common tasks. Making an app is quite straight forward, and all you need is Leiningen [2] and a JDK. For example, you could do:
lein new luminus myapp +postgres +auth +swagger
This will create an app using Postgres as the database with a Swagger UI set up out of the box.
During development mode you'd run the app with:
lein run
Any changes you make in the source will be automatically reflected when you reload the page. You can also connect the editor to the REPL that gets started on port 7000 by default.
You can package this app for production with:
lein uberjar
and you can run the resulting jar as:
java -jar -Dconf=config.edn myapp.jar
You really don't have to hand roll your app and go hunting for libraries unless that's something you want to do.
> A lot of dependencies tend to stop being maintained after a while. [...] The newer shinier ones lack in features while the more featureful ones are from 3 years ago and have not seen a single update since.
A lot of libraries are not updated because they do not need any updates. This is surprising to people coming from other languages, but is fairly common in the Clojure world. I use many libraries which haven't been touched in 3-5 years and it is fine: they don't need updates.
> Auth systems etc. are half-baked with 10 different implementations on GitHub
Here I would agree — authentication is a problematic area. I use buddy, which I had to integrate into my ring+sente app manually with quite a bit of pain. Friend has the wrong abstractions, IMHO, and does not get the job done.
Don't write a wrapper when using a Java lib. Just use it directly. Now the next person coming in just has to use it, no need to understand the wrapper and extend it. Interop is a first class feature specifically for that reason.
For the rest, I can only say that I don't understand the sentiment. It's true what you say about lots of Clojure libs, they were one off projects from someone and work has stopped on them. But I am able to move fast and ship with Clojure. For me, I can actually move faster with it. That's one of my main reasons for liking it so much.
There's a Java lib for everything complex. And there's a quality Clojure lib for everything common. I've never not found what I needed. And in general, I don't need as many things because the core libs are so full featured.
I'm not trying to deny your experience. It just makes me curious how my experience can be so different and almost opposite. That's why I've concluded that it's just not for everyone. It seems you are either a FPer or you aren't. And you are either a Lisper or you aren't. And with Clojure, you have to be both a Lisper and a FPer.
It has less to do with FP or Lisp and more to do a lack of emphasis for rapid development and a significant impedance mismatch with the rest of the ecosystem. JVM interoperability works when the API is clean. It doesn't when obscure OOP features are used three layers deep and have to be mapped into Clojure which is more functional in nature.
The sweet spot for clojure is data oriented/data processing backend services (data engineering), where you can leverage stuff like kafka, spark, etc. You are going to get frustrated if you use it for common CRUD websites where the node.js/ruby/python ecosystem actually has quality libraries for all the common parts. In most cases I wouldn't trade node.js for clojure when writing front-end servers.
Fulcro can be too sizable to consume in one bite, most importantly the three core concepts (ident, query, state). Personally I find it useful to start with just EQL which is an Clojure native alternate to Graphql. In fact, Fulcro's predecessor, the famous Om.next was inspired by Graphql.
Introduction by Wilker Lucio https://www.youtube.com/watch?v=yyVKf2U8YVg
Somehow I got the feeling of there being a lot of boilerplate necessary to create just about anything. Still this is years ahead of the competition (excluding re-frame).
Yeah, long require forms, long namespace keywords, protocol methods... but they are all necessary so Fulcro can figure out a lot of things itself. It pays off really well because things that are often hard in other frameworks such as forms and server-client communication suddenly become easy as a result.
In addition to the book, Fulcro has excellent docs and resources available:
* Full-stack example [0], implementing the RealWorld spec using the Walkable SQL library and the Duct server-side framework
* Fulcro's implementation [1] of UI state machines (recent HN discussion on the topic [2])
* Fulcro training video series, from the creator Tony Kay [3]
* An answer to why Fulcro [4]
* How Fulcro differs from Om [5]
* Fulcro's integration with the Semantic UI React toolkit [6]
* Where Fulcro is headed next, in v3 [7]
To me Fulcro is Clojure's missing framework. With Fulcro (and thanks to Clojure/Script), applications are composed and painted onto the screen -- this is thanks to the REPL and hot-reloading that preserves state. For a demonstration, see the Fulcro training playlist on YouTube [3].
Some personal favorite Fulcro features: (A) the built-in support viewer [8], which can take state history serialized on the client and play it back on a developer's machine (like a basic, self-hosted https://logrocket.com). (B) Workspaces [9], which is similar to https://storybook.js.org.
Does anyone have experience using Fulcro just for frontend (no backend)? I have been considering it versus Reagent and Rum for building a heavy client-side app that doesn't need a server (nothing to store in a database, local storage is fine). At some point I might add users for data sync between client and server but I don't plan to do SSR for SEO purposes at least.
What I am curious about is if I use Fulcro, will I write a lot more code to do the same as in Reagent/Rum because it's supposed to play well with a backend that's (for now) non-existent? Does it provide better tools for client-side state management compared to Rum/Reagent (+ re-frame)?
Right now I am using just Rum with its built-in cursors and derived atoms and the experience is okay. I did try DataScript but ended up just with a simple atom for now.
As an alternative, modern web framework for Clojure, I recommend Coast https://coastonclojure.com/ (I have no other affiliation to this project other than using it myself).
Time travelling immutable universal databases are their own reward, lol
http://www.hyperfiddle.net/ is probably the closest proof of concept I know of right now, some interesting properties are solved N query problem with smart and pinpoint caching, infinite TTL caches, SQL injection impossible, client exposable query language
Aggregate shapes defined at query time, not at insert time, it's mostly about moving immutable Datoms around not trees that are hard to reconfigure & reuse like nested JSON
I'd suggest looking into the feature set of Datomic, and then ask yourself what would happen if you ran with those concepts in the front end? - Fulcro
Clojure is awesome. But refactoring a big project without a type system is very hard and error-prone, and clojure error messages are very difficult to understand.
I'm a static typing person, too, but considering how wildly popular dynamic languages are for Web development, it's hard to take blanket criticism like this seriously.
As far as large projects go, my own experience has been that the dirty truth is that, past a certain size, nothing is statically typed, anyway. Either it's decomposed into a bunch of smaller services that communicate using a weakly-typed messaging system, or it's a monolith that resorts to some sort of stringly typed mechanism for communication among the major components. I know the speakers at tech conferences say we're supposed to keep it strongly typed and use a ports and adapters pattern to limit the scope of impact for type changes, but that just doesn't seem to be how it ever happens in practice.
It makes me think that the Clojure folks might really be on to something, and that, for large business applications, static typing is trying to solve the wrong problem. I'd personally love to get a chance to take its specs system for a spin.
This is true BUT data is easy to refactor and Clojure is really about Data. Clojure's big picture is a principled mental framework for reasoning about data, and as such gives us the core vocabulary and utilities we need to write programs that operate on data in a principled way (immutable data structures, immutable database etc).
So Clojure IMO is today in this awkward spot of saying "Here, use these new principles of data to go solve problems in this new better way!" but nobody has really deeply figured out what that even means. The whole middle ecosystem layer has yet to be written.
But it's happening, there's a movement of believers coordinated by Rich's talks and working to this common goal. And when it works, the whole 100k+ loc system that can't be changed will be but a memory.
(I am Hyperfiddle co-founder, Hyperfiddle poses the question: is it possible to express sophisticated database applications out of just data? We think we've made enough progress to show that the answer is probably yes. Hyperfiddle apps are not stored in git but rather in Datomic. And for special cases where you do need custom code, you can just drop down a layer seamlessly into Clojure.)
In my experience the new spec system (a sort of gradual typing) greatly helps in both refactoring and both surfacing and explaining errors, if you havent worked with clojure 1.10 I’d recommend to give it another go.
Also spec is still in alpha so it will keep getting better.
I disagree — I am refactoring a large project (60k LOC, that's a lot in the Clojure world!) continuously and find that a type system would just get in the way. On the other hand, clojure.spec together with :pre/:post conditions works great and helps me a lot by checking things that no type system would verify (such as validity constraints on entire data structures).
Error messages stopped being a problem with Clojure 1.10.
Clojure has a type system... but I know what you mean. Perhaps you could expense a copy of Martin Fowler's new 2nd edition of Refactoring? It uses JavaScript, so applying the lessons to Clojure will be even easier. Refactoring in dynamic languages just isn't an obstacle for some of us, I don't know why, but that book is the only thing I can think of that might help someone having trouble bridge the gap.
And then I discovered Clojure and Ring and later Liberator. What I like about my current setup is that I have full control over my framework and my team knows its ins and outs and the corresponding Clojure code is succinct enough to allow developers to come upto speed quickly. Our set up is not sexy but it works and we know how exactly it works.
I am not sure what I would miss if I did not use a framework like this. Is Liberator no longer good enough ?