I think that Clojure provides a number of advantages both in terms of language design and ecosystem, especially in the domain of services and web applications.
In terms of ecosystem I would say that having Clojure and ClojureScript is a clear advantage. You can use the same language on both the server and the client. You get shared code, shared tooling, and shared data structures. All the devs on the team are now also able to work with the entire code base, and you can easily move logic between frontend and backend.
Since Clojure doesn’t have popular server-side frameworks, it’s a common pattern to manage the UI with client-side frameworks like re-frame and implement the server side as a stateless service. On the other hand, Python frameworks tend to be focused more around server-side rendering. Treating the server as a stateless API makes it easier to add other clients such as mobile apps, and to scale horizontally.
Another piece of tooling that makes Clojure development more productive is the editor integrated REPL. Any code you write, you evaluate in the REPL straight from the editor. The REPL has the full application state, so you have access to things like database connections, queues, etc. You can even connect to the REPL in production. So, say you’re writing a function to get some data from the database, write the code, run it to see exactly the shape of the data it produces. Then you might write a function to transform it, and so on. At each step you know exactly what your data is and what the code is doing.
Having a live development environment makes natural to do exploration. If you’re solving a problem and you don’t know the best approach, it’s easy to experiment by running the code as you’re writing it. You can quickly try a few different approaches and settle on one that works best. This also affects refactoring, as you can immediately test what you did. The REPL is also very helpful when you’re looking at new code you’re not familiar with. By running things as you read them you can confirm that you understand the code correctly.
I think that dependency management story in Clojure is much better. And packaging applications as uberjar results in a single artifact that only requires the JVM to be run. It’s basically like having all your apps running in containers for free.
Clojure also has a number of advantages as a language. I would say the main feature is defaulting to immutability making it natural to structure applications using independent components. This indirectly helps with the problem of tracking types in large applications as well. You don’t need to track types across your entire application, and you’re able to do local reasoning within the scope of each component. Meanwhile, you make bigger components by composing smaller ones together, and you only need to know the types at the level of composition which is the public API for the components.
Next, immutability makes it possible to work with threads in a sane fashion. Clojure provides excellent parallelism and concurrency primitives out of the box. Python provides nothing comparable in this area.
Thanks to immutability Clojure APIs tend to be data driven. API functions take data as arguments, and return data as a result. You don’t have to worry about any hidden behaviors like you do with objects because data is inert.
This kind of isolation also tackles a lot of the same problems as microsevices. One of the main motivations behind microservices is to reduce coupling. With Clojure you can think of each namespace as its own microservice. You have a set of functions that you call, each function takes an input and produces an output. So you have similar benefits during development time, but you don’t have to pay the cost of a orchestrating many services in production.
I recently saw a Cognitect presentation where they used Pedestal as an example application. One slide shows that over 90% of the code is pure. All the state and IO accounts for less than 10% of the application. This is a completely normal situation in Clojure. [1]
Meta-programming in Clojure is strictly superior to what’s available in Python thanks to s-expressions. You can take any piece of code and transform it like you would any other data structure using the same language. Being able to treat code as data also means that you can serialize and load code for free.
Immutability and s-expressions combined lend themselves well to refactoring. You just select the expression you want to factor out, and pull it out into a function that takes the same arguments.
Finally, I think that s-expressions provide superior editor experience thanks to structural editing. When you’re manipulating Clojure code you’re thinking in terms of blocks of logic. When I use other languages I tend to think more in terms of lines of text.
[1] https://www.youtube.com/watch?v=0if71HOyVjY