alt.hn

2/26/2026 at 1:31:08 PM

Show HN: I built a self-hosted course platform in Clojure

https://clojure.stream

by jacekschae

2/26/2026 at 1:31:35 PM

Hey HN — I'm Jacek. I've been teaching Clojure through video courses (Reagent, Re-frame, Reitit, Datomic, Pedestal) for a few years, hosted on Podia.

I finally got around to building my own platform. It's Clojure on the backend, self-hosted, and gave me the chance to drop prices significantly since there's no platform cut anymore.

The migration from Podia is complete — all user accounts, subscriptions, and purchases carried over. Existing subscribers had their prices automatically lowered.

Happy to talk about the stack, the migration process, or the courses themselves.

by jacekschae

2/28/2026 at 4:18:02 PM

Congratulations!

I looked into Clojure a little bit, and my understanding is that it is a dynamically typed language and objects/values are formed on the fly via putting data into the a map (reminds me JS). And as someone having experience in both statically and dynamically typed languages - I definitely choose the first. My question is, how do you deal with this? Isn't dynamic typing getting on your way? How do you define contracts? How do you version schemas? Do you even have them? How do you know what to expect from the database? Or how don't you forget about mandatory fields in your objects?

Dynamic typing is scary. I had a production issue in NodeJS codebase manifesting only when two pods of the same service were fighting for updating the same resource in the cache. One pod gets lucky, updates the cache, and returns an array. Meanwhile, the same function, on the other pod, returns a map, because it failed to update the cache. And because both the function and calling side had no "contract" on what to return and what to expect, the runtime was happily converting my array into a map, and that was causing a huge problem downstream. Imagine how enjoyable was debugging this... Point is, this situation seems impossible in a statically typed language. I wonder how this issue could have been avoided in Clojure.

Thank you.

Edit: One-time code goes into spam folder on fastmail.com.

by vagag

3/1/2026 at 7:35:12 AM

you can do validation/"contracts" at API boundaries using spec or malli. Theyre more feature-rich than types

(I havent had to personally use them though)

by geokon

2/27/2026 at 11:12:07 PM

Could you tell more about the stack? And also, you clearly know your way around it, so would be curious to hear your thoughts about the language itself - what do you like about it?

by tommica

2/28/2026 at 12:09:54 PM

Congrats. Always wanted to get into Clojure.

Did you use AI to help code the site?

Some feedback:

- Scrolling is slow. I'm on Firefox. It might be the background image or some other effects?

- Pricing isn't shown in USD but instead in CHF. Not sure whether users would want to go the extra step and do another search to do fx conversion.

by uxcolumbo

3/1/2026 at 10:55:42 AM

Why should they prioritise US currencies? How about EUR or GBP? How about just do the conversion like the rest of the world has to do for USD

by lwowowx

3/1/2026 at 5:41:03 PM

How about detecting which country you're viewing this from or letting the user to change it?

by uxcolumbo

2/28/2026 at 4:18:59 PM

Interesting, scrolling is smooth on my side (wayland + firefox 147).

by vagag

2/27/2026 at 10:44:43 PM

Is the source code available? You mentioned it being self hosted, thought I might be able to find it on GitHub but didn't see it linked anywhere.

by nickjj

2/28/2026 at 1:40:11 AM

It might not be; I did a couple of GitHub searches in their organization for strings on their homepage, and found no matches.

by olivia-banks

2/28/2026 at 6:18:35 AM

Love the colors

by sakesun

2/27/2026 at 10:41:05 PM

snappy!

by EToS

2/28/2026 at 5:44:26 PM

Hello thank you for this. Off and on I have looked into Clojure/Clojurescript but in most day jobs I haven't been able to use it or implement it. Looking forward to the courses!

by ducktastic

2/28/2026 at 1:14:09 AM

[dead]

by selfhosting_sh