Andrey’s migration highlights a strategic shift from Clojure’s dynamic flexibility to Gleam’s structural rigor, prioritizing long-term maintainability through end-to-end type safety. It’s a compelling demonstration of how modern static typing can effectively reduce the cognitive load of managing complex, decade-old production systems.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
I rewrote my project from Clojure to GleamAdded:
All right. So, it was 2012, I think.
I was kind of first year in my software engineering career.
I think around that point I decided I need to upskill my coding level level and I wanted also to create some project that I can call my own. And I was thinking about what I can do. You know, it's a really hard one to decide a good idea for a project. I still don't think that's like best idea I could do, but that's what I decided to do anyway. So, I created this website called startupfellows.ru.
So, it's literally in running in production, as we can say, for over 10 years now. And the idea is that you can, you know, like post your startup idea, then you can search for co-founder or a person who is interested to join you. Maybe you're a developer and you search for a UI or a project manager, having an idea and want some developer, and they find like a pair and they can work on something. So, there were projects like that already in Russian-speaking segment, as you can see that the website is in Russian. And there are also big projects in globally, like uh co-founder lab or something like that. Most of the projects that were in ru segment at that point already dead by this point of time, but mine still running. Um So, so yeah, that's the idea.
The goal of today's video is to talk about the recent refactoring or rewrite that I've done from closure to gleam.
And before that in the closure version wasn't the first one either. So I started as a Java developer. The first version was actually in Java spring I think. Then I thought it would be cool to learn functional programming and I found closure and the main major rewrite for was from Java to closure. And the closure version was recreated multiple times updated improved over the years.
But last year I just decided to do a major update. I found some issues that were really frustrating in closure.
I was doing closure for over 10 years now. I wanted something new and I decided to go to a different stack completely. To get some knowledge and to have a new experiences. So that's what I'm going to talk about today.
So let's go quickly through the the website. As I said like there there is a section for the where you post your job descriptions. As you can say like vacancies something that and then there is like a resume section the opposite side when someone is looking for a project. And then you kind of have a general list of users on the website.
And this startup sections is actually new that wasn't in the closure version of the site that was purely created with in gleam and also with a lot of help from Claude. So it's you can see look at this as a kind of white coding experiment but with a lot of human uh, because I didn't really want um, AI slope in the code base. I just want to keep that under control. So, I think the results are pretty pretty good. So, you can uh, have a list of your projects. There are new and best and you can vote for them. Um, if you go inside, there is like a vote and you can see who actually voted. So, that's look pretty good to me.
Uh, I also added cross links. So, uh, like the the vacancies and also here in the vacancy, if you go inside, uh, there is a similar stuff that's suggested. Uh, so, you increase the user involvement into the website.
Anyway, it's this video is not about the product side of these things. It's just about technical refactoring from closure to gleam. So, let's jump straight in.
Uh, one thing I want to show about is that it's actually life. So, that's the uh, metrics for the last uh, quarter.
I've got around uh, 45 43.3 uh, thousand views.
Not too much, but as you can see, uh, people are using that and um, that actually increased the complexity of the rewrite because you want to minimize the user impact, obviously.
Um, even even even when the sta- site is down, um, after an hour or so, I usually get pings and messages from people through different channels saying, "Hey, why the site is not working?" So, yeah.
That means it's actually used.
So, let's jump to the coding editor.
Um, CLJ uh, SFSLJ, that's the name of the old project old old repo, uh, which is closure. And uh, let's do this, and I use this tool key tool, and it will show you the lines count. So, closure uh 5K lines of code.
Uh CSS you can ignore because that's some Tailwind large files included and some old CSS that should be removed long time ago.
Uh the other interesting thing here is Elm, and that was one of the uh periods when I was adding more uh front-end features to the website, and I decided to try Elm.
Uh I thought it's quite cool idea, and actually Elm is one of the reasons why there is a refactoring to Gleam because I wanted to continue to develop more front-end bits.
I've checked earlier this year what is the state of Elm, and I realized that there is no new releases since when I was doing the changes here.
And in one of the Reddit post I've seen that, you know, like if you're looking for something similar to Elm, look into the Gleam front-end like client-side with Lustre.
So, that's like similar idea, but it's more maintained right now, and feels like more future-proof.
So, that was my first kind of hint why I want to try Gleam.
Uh and also I wanted like let's talk about the issues with with the code base a bit later. So, that's what we have right now. Um Yeah, so pretty standard closure stack Um if you're familiar.
I have ring, I have composure.
I have Java JDBC, next JDBC, and Honey SQL as a database integration layer. There's also HugSQL, an attempt to use that way of interacting >> [snorts] >> with a database.
The problem I got is um that that that that that project that closure project was kind of my learning curve how I got into closure and I was trying different things even before I started doing closure professionally and I gained a lot of uh professional closure experience. So, right now when I look into the code base, I'm not really happy with a lot of places. So, let's say if I go to um the uh queries, uh SQL, like any any of those.
So, this way of uh combining and building queries is really hard to maintain uh when you have some kind of base query and then you have a chain and then you use a DSL to add more stuff there.
So, the better way is just to use a plain Honey SQL format. So, let's say select um something from table name etc. So, I prefer that much better.
But, even when I started refactoring this, I realized I'm using really old version of Honey SQL.
Uh and then I'm using old Java JDBC.
Uh, when I tried to update that to the most recent version versions, kind of everything is is broken. And here's the main point. I never created any tests for this project. So, any change is really risky. So, the the real um, approach, if I want to keep closure project and I do a major refactoring, uh, is to cover everything with tests and then do the refactoring and then fix the tests and iterate like that.
Uh, to be honest, I was I had enough closure experience, so I I know that will work and I know that eventually I'll be happy with the result. But I still, you know, didn't really like um, that I have the Elm section on the front end I have the closure in the back end.
I wanted something like some really new experience.
And that's why I thought the Gleam approach where you will have um, front end in Gleam, back end in Gleam, and then you have the shared layer, shared module as a glue between those two will be really nice.
And I also was really um, excited by the idea that, uh, you have a type system that's actually not slowing down you, but actually really helps. Um, because in Gleam it is strict type system, but you don't have to define types everywhere. The compiler is smart, it can understand strictly what type is here and then it enforces it through the code base.
And uh, if If extrapolate that to the client backend and the middle layer, you have a type safety from the database up to the client.
If you change a field in your database table you run the generation of your SQL layer you run the build you will see the issues uh in the front end. You'll see the issues in the backend everywhere. And that's what I was missing in closure.
Um in my day-to-day job I was doing closure many years and I think in a lot of domains when there are a lot of uncertainties, third-party integrations, stuff like that, a dynamically typed language is actually really cool because it allows you to move really fast. If you do good test coverage um and stuff like that, if you have a team working on microservices, dynamically typed language is really cool. Uh the only thing you need probably is to define a good uh contracts between. So like uh open API specs uh Kafka schema registries, stuff like that. Everything else is really good.
But this is my personal project and I don't have a team. I don't have too much resources. I just wanted something from the uh tooling ecosystem that I use that will actually help me without too much stress.
And um moving forward, as I can say with the results of the refactoring, I was really happy. Uh most of the times uh when my project was compiled successfully, I was pretty sure that it will work when I tested and I never seen a problem with that. It's really like that.
You have a successful compilation, you spin up your Docker image, everything there, everything works.
Um but I also didn't want to, you know, do the same mistake as I've done in closure and I wanted to include tests.
So, initially, uh the idea was to have a good test coverage as well. And that was really helpful because, you know, type safety type safety don't prevent logical bugs. So, yeah, tests are really important still, but in modern age, you know, code producing is not the, you know, hard part. You can use Claude and I I use Claude for the refactoring.
Uh and it worked really well.
Uh and also, yeah, again, you describe what you want from your test or even talk to Claude, you define the contract, and then the generation of code is, you know, you just wait until Claude is is doing that, and that's it.
All right, so, four and a half thousand lines of closure code, um let's move to Gleam.
So, that's the Gleam project.
And you'll see, oh, wait.
Talk here. It's much larger code base here.
Um two uh 20,000 lines of code in Gleam.
That's front end, back end, and shared.
Uh but also, it's not uh true comparison because, on top of the original project, I was able to introduce a lot of features. So, it added up maybe third of the lines of code, but I'm not saying that it was one to one from the initial refactoring. And we'll cover some whys why that happened.
So, yeah, as I said, and I have a separate video about that.
Uh there is a Gleam kind of web development tutorial on my YouTube channel. You can check that out.
I structured in this way. So, you will have client, we have a server, and then we have shared in between.
Let's move to um drawing board, and I'll describe how I approached the the refactoring. So, that's client, and I'll have server here.
Server.
Inside server, there is a database. And I always just use Postgres.
And it works really well.
Inside Postgres, not inside, but related, um there are SQL queries.
And when you do a large refactoring like that, uh it's really important to draw some lines and uh say that you don't, you know, bloat the the scope too much.
And one of the core ideas of my refactoring was to build a new Gleam application on top of existing uh database schema. So, I just picked the Postgres schema as is and put it in the new foundation of the project. Later, I was doing some optimizations and refactoring in the Postgres schema because I wasn't happy about that as well in some areas. But for the initial refactoring, I tried to keep it as is.
So, SQL queries really important. Um that's basically your access buttons to your data. You need to think about what you need like get startups, startup by ID, get resume by ID, get vacancy by ID, get profile by ID, then some uh full-text search buttons, inserts, upserts, deletes, all that stuff.
So, you have a list of queries [snorts] that are pointing to your database. And here the beauty of Gleam X system begins because there is a uh Squirrel library. And it is the code generation generation library. It needs two inputs. First of all, in the server, you have the uh SQL folder. And here it is just uh plain SQL files, nothing special. You can use whatever syntax you want here. Uh everything that your database supports.
So, yeah.
As you can see, bunch of different queries. But that's fine.
Uh again, writing code like this is a cheap button nowadays. Uh AI can handle that.
You can just describe the button you want, it will produce correct query.
So, that's the the queries. That's the source of truth how the app is uh interacting with the database.
Second part is you run your actual database in a Docker container or somewhere, probably locally. That was I was doing.
And then you point your uh Squirrel generation to the SQL folder and your database. And it will connect to the database. It will read your files. It will understand what tables you want to query. It will go get the DDL like the definition of your tables from the actual database.
It will know the types of the fields and it will be able to generate a gleam file that looks like this.
SQL.gleam.
It is a It is a huge one. So, I think it's like Where is it?
Um talking minus F sort lines. Um It's a top one file in terms of size of 4K of code lines.
But uh it is all generated code. So, you see it's here. Type This type definition was generated automatically using this version of the squirrel library.
It will generate your return types and it also will generate you um functions that you can just call from your code. Select uh not type a one function. Select user by email, right?
That's function. It will get um an argument um like a user ID. It will uh Where is the query? Yeah, that's the query that it will run. That query was originally written in the just SQL file.
Uh squirrel library just got it, wrapped it in correct gleam uh code and then it's doing some decoding of the uh return type and it's returning um uh this this generated type. Select users by email role.
So, after that uh you build on top of that and you have the type safety from these types up to the the client.
But, the question is how you actually do that.
And uh the thing is that you can introduce the shared module.
Uh let's call it just shared, as I call it. And inside you have uh Gleam code as well, and it will be your shared types.
So, for example, if I go to client and let's go somewhere.
Uh post comments. So, that's the section that uh responsible for the form when user can create a new comment and send it to the back end through the API.
And here you see the import, and it's actually an import from the shared module. So, inside shared module, I decide I defined a custom type, which is comment.
Uh Gleam uh the the SQL SQL libraries that we saw previously is really generating types one-to-one mapped with um with the queries that you defined. So, even if that's like returning the same kind of entity in a nutshell, let's say you have two queries selecting from the same table.
Uh it will return more or less the same data. So, logically, it's all, for example, comments.
But, uh in SQL, they'll be different types because the type will be defined by the query name. So, the idea of the shared types is to have um uh well-defined mapping that you manually create.
Uh and then you can map your uh SQL types that uh were auto generated into those shared types and then use those shared types across your stack. So, you'll decode uh uh you'll encode your entities with this type in the API responses and then the client will read it read them and it will use the same uh type decoder uh to decode it back from JSON to Gleam um object.
And what basically that means is that you define your encoder and decoder and you reuse them from two sides of uh of your stack and that means nothing can break because that's tested, right?
Also, uh you you just define this type and there are two functions, to JSON and from JSON basically.
It looks like a lot of codes. Uh the good news is that you don't have to write that write that by hand. You can just rely on code actions. So, if you go to code actions, that's uh uh what language server or LSP provide provides for this particular code uh block. And you can see you can just run generate dynamic decoder, generate to JSON function. It will read all your type definition and it will generate a proper uh prop function. Sometimes you need to fill some to-dos that uh that generator leaves in some places, but it's like 1% of uh the code that you need to write for encoder and decoder.
All right, so that's the the shared thing, right? Then we have interaction between front end and server.
It is uh uh API and I used just JSON format for for that.
And this is using the shared types.
So, decoder here will be used from the shared types decoder, encoder here will be used for the uh shared type encoder.
But, also we need to cover that gap between uh SQL uh database types and the the shared types that we introduced. And that's why I I created the mapper file.
And here there's just a bunch of functions that will map all available um SQL types into shared types. So, you see it accepts the the row, which is the SQL type, and it will return the user.
Um It's literally just that. Again, it's a big file, but most of that was AI generated, so you don't have to think about that. But, what it gives you is it gives you the end-to-end type safety because if you require a new type in a shared type and it will be missing in your mapper, it will complain.
Or like if you add something in um your database, like a new column, and then you want to use it in uh front-end, you see it's missing there. You go, you understand that you need to update the shared type, you update it there, and then your code fails because you need to update the mapper. And that's basically how it works. If you made a mistake in a type, again, it's uh type strict type safety, uh so it will be detected as well.
So, also regarding the the refactoring um I just use the the There is basically two sections. One is the UI that generated here, which is static HTML returned by the server.
And that's the I would say 80% of the the application of of the website because it it really depends on the search.
And I wanted most of the pages that are available without logging in, I want them to be statically generated, so they are indexed by search engines really well.
And the other thing is here, right? So, uh dynamic front-end code.
And that's for the sections like user profile, managing of the profile, maybe managing your uh submissions like your project, your jobs, your resumes, and stuff like that.
That this section.
Um during the migration, I just grabbed basically plain HTML as I have it in my existing website.
I just put that in HTML files, and then I asked Claude to rewrite that in uh Gleam with uh Lustre uh framework. And Where is it?
Uh layout, I think. Yeah.
So, this is how HTML looks in Gleam.
Um similar to Elm, probably. And also, it gives me kind of same feeling as working in Clojure, to be honest, with Hiccup. Yes, it is function calls, and then you nest stuff, but it's uh kind of same tree structure, and it's really easy to understand.
What I mean, once you understand it, it's really easy to read, right? So, you you have an like an HT- HTML node, and all of those nodes are defined in um list um where is it? Yeah, elements HTML.
Uh they will have a bunch of stuff here, like your H1s, basically everything or I I I guess almost everything that's available in HTML will be a function here, right? And um that's the first first thing, and then it will take two arguments. First one is attributes, and the second one is the children. Uh in the children, you just have an array of the nested stuff, like additional elements that you put inside your HTML tree nodes.
Um the first thing is you can put any attributes there as well. So, the most commonly used one is obviously class to do the styling. And I was using uh Tailwind in both uh Clojure version and the Gleam version, because I really enjoy as a developer how easy you can, you know, create more or less good-looking websites without touching raw CSS.
So, yeah, class is one of the main attributes, but there are I'll other attributes available, so like href, src for the scripts and stuff like that. But it's it's really easy to understand how to write things. And then you basically you know, generate some functions that define smaller sections of your UI. You have a function that gets the input data like your types from the database and it will produce the uh Lustre HTML-like structure. And then at the end you just convert it to HTML with some functions available in Lustre as well.
And that's the same code could run on back-end and front-end. So, back-end Gleam is compiled into Beam. So, like it's same as Erlang, stuff like that.
Front-end is obviously JavaScript. Uh but the code that you create is the same in the back-end and in the front-end.
So, yeah. Like refactoring was two two major things, right? Make sure that you your database is the same.
Create SQL queries, generate the code that interacts with the database.
Then uh import all your HTML. Ask AI to split that into layout.
Uh common things like item in UI that describes your project, stuff like that. And then just combine that together.
Um create the routing routing table like your pages and stuff like that. And then just link that together. And again, as I said, I tried to use test tests um much more here.
And, uh, especially uh, test the security aspect because, you know, AI can, uh, ignore stuff like that quite easily. So, I wanted to uh, protect, uh, admin routes. I wanted to make sure that, for example, if you have a, um, project that only you can access the edit pages.
Uh, all that stuff. And I tried to cover that in a test.
Uh, in tests, uh, um, as good as I can.
Um, a guy, uh, about them, um, developer experience, right?
So, although I I really enjoy Closure, I really like the the the tooling, especially when we're talking about the line again. So, it's like line you and you add dependence and stuff like that.
My personal opinion that, uh, the new Closure tooling, CLJ and stuff like that, uh, is not the right direction. So, yeah, I don't know. I Like a lot of people disagree like and there are, um, you know, people say that, you know, it should be extensive, uh, extendable. You you you can, yeah, like it it gives you kind of framework. It's really flexible, blah blah blah.
That's not what I want from the tool.
And I'm I'm pretty sure, I don't know, most of the developers that are focusing on producing some products and building code, they don't really want that. They want, uh, a tool that works.
And I think the the Gleam command line thing is one of the best things that I experienced. And also their LSP. LSP is really cool.
So, the only thing you need is Gleam, and then you have all the commands. So, you can add dependencies, build, check, clean, format, somewhere. Yeah, format.
You know, update even.
You don't need to add any extra packages or any extra tools. So, everything is is in here.
You just uh build your own commands as you as you need them, and uh maybe like, you know, some wrapping around that.
And that's it.
So, for me, I have the I'm using the Mise tool, Mise Toml to manage versions and also to um use it as a task uh runner, task manager. So, like you you can think about it as a makefile uh alternative.
And here I have a a bunch of tasks. So, let's say I want to uh Mise run something. There's the list of things I want to run, let's say, client checks.
It will go to It will run just Gleam check inside the uh client folder.
If I want to run something else, like I I have a bunch of stuff, like client test, for example.
Like test. I I I don't write too much client tests. I need to fix that. But, uh to be honest, um I spent a lot of uh effort uh reviewing the back-end code, because that was my area of interest.
Uh most of the front-end is primarily wipe coded, uh I should admit, but it works.
But, the code quality is not uh up to the standard uh I wanted.
But I'm back to the back to the tasks. So, bunch of things, right? Even the the SQL generation, right? Gleam run minus squirrel, and that will run the the thing that I mentioned you. It will look into the SQL files and connect to the database and regenerate the the Gleam code.
And at the end I have the mix run all that will run Where is it?
All. Yeah, here we go. It will run format, check, and test, and each of them will run a format, check, and test on each of the sections of my application like server, client, and shared, and that takes only 3 seconds. Most of that is just tests.
Um for the local development, uh I was just spinning the uh database in a Docker container, and then just run the app on top of that. And there is, for example, um watch exec wrapper. Uh so, for example, for the server, right? I I I I use watch exec. I'm saying that I'm looking into changes in the dot Gleam uh files, and I'm doing the the restart and rerun the Gleam run. So, if I do mix run um server, it's running Gleam, and if I change something in the code, it will automatically restart the the app, and it literally takes half a second, uh maybe less.
Um It gives you almost instant um developer feedback when you're developing stuff.
Um in closure it's different, right? So, if you try to do similar thing with uh restarting the app from the CLI, you'll have hard times because it's Java and it's slow for cold start. But, um um I don't complain about that because they in closure there's a different way.
You spin up the repple once and then you have the reloading inside. So, you can just have a command inside that will refresh your code and reload the app.
So, that works really well as well. But, uh this one is also really good. And I I enjoy this way of working as well.
Again, the biggest win for me was uh type safety. And I'm not saying that dynamically typed languages are bad. I'm just saying that for my personal project here, I I really benefited from having a nice type type system and nice tool that actually helps me.
I'm still using uh dynamically typed languages primarily at my day job. So, it was 10 years of closure. Now, I'm doing Erlang, which is also dynamically typed.
So, I enjoy that as well.
But, this was a really nice introduction into a language like that.
Um And also, you you know, like it it it's not the goal of covering all the features of nice features of Gleam, but it's really tiny language, really concise, but it's also well thought. So, there's like no exceptions. You need to work with result types and optional types.
And it all just clicks really well and it all just works. So, if you interested, let me know in the comments and also yeah YouTube really likes if people post comments.
And it gives the understanding of activity, interest and stuff like that.
So if you want to support the channel, the best way is just to write a comment.
The second best option is to go to buy me a coffee and uh support me there.
All right. So yeah, that's it for the video. Hope you enjoyed it. See you next one. Bye-bye.
Related Videos
Agentforce NOW AMA: Build with React and Salesforce Multi-Framework
SalesforceDevs
490 views•2026-05-28
How agent o11y differs from traditional o11y — Phil Hetzel, Braintrust
aiDotEngineer
450 views•2026-05-28
WEB TECHNOLOGIES UNIT-2 | Degree 4th sem BCOM Computers web technologies unit-2 full explanation💯✅
LearnwithSahera
1K views•2026-05-29
More tests are always better? How to use AI to identify tests that bring little value
Alliance4Qualification
335 views•2026-05-29
Search Algorithms Explained in 60 Seconds! 🤖💨
samarthtuliofficial
218 views•2026-06-01
People of Game of Thrones using JavaScript DOM
AltCampus
296 views•2026-05-30
Introduction to Problem Solving Part - 1 | Lecture 1 | Intermediate DSA
ascensionix
107 views•2026-05-29
🚀 BCS613C Compiler Design | Module 1 to 5 Schema Evaluation 🔥 | VTU 6th Sem 💯 #VTU #bcs613c #exam
Pranavaa-y4y
104 views•2026-06-02











