Dante forgot to describe this one

Recently I was asked to help out an old project to get them unstuck. As I was helping them and fixing the problem, I who follow the Creed, saw how the state of some parts of the codebase were dilapidated and so according to Principles we fix them. This is the way.

CI

The state of the CI was a bit to be left desired. They ran out of date images from 2 years ago and never got them updated since that part was not being maintained anymore by my previous employer. So I snipped that connection by moving in some more ci defined images in the repo. This made it maintainable again and moved them into AWS ECR us-east-1 region for better compatibility with CircleCI. As per their advice.

I did see however that the flow was taking up a lot of credits unnecessarily, due to the fact they (well I back then) split up a lot of steps to make it more in parallel. This however hardly saves time and what it does do is add a lot of startup time and credits. So after bundling things together for the backend and simplifying the flow a lot, there was one glaring issue remaining.

The frontend. Da da da daaaaaaaaaa

First circle of hell

The first circle of hell is a thing called frontend dependencies. So the frontend word just moves forward everyday like a death march. The problem is sometimes you want to move ahead but they introduce an incompatibility between two minor versions of their own published packages. So you get bogged down into these vague errors and problems and you are left wondering what portal you stepped through to get into this Twilight Zone.

This other dimension that seems very much like your own, but something is off. Some minor thing, you cannot put your finger to it and explicitly say it, but something is off. Just something minor, colours are slightly different, the keyboard you use to press in the keys feels like it has a slightly different actuation force. It just turns out it has to do with a slightly different package version and NodeJS runtime and suddenly you are in purgatory.

The problem also arises when devs are frozen in time. They have their node_modules cache set to the date of when the project began and it will work till end of days as long as you keep those files exactly like that.

This same frozen time capsule idea is used in CircleCI with caches. So naturally when I started to work on this, nothing worked anymore. To resolve this takes the delicate touch of a surgeon because if you approach it like a butcher you will end up murdering the project.

Second circle of hell

The build times are so slooooooooow. I cannot emphasize that enough. Why are they so slow, who knows. Well diving into the react-script codebase it reveals itself naturally. They run everything inside of Node, which is not multi core or multi threaded and then also they do their own implemented stuff which the standard library has a solution for already, because reasons.

Now this meant that I was naturally limited by this horrible implementation, but still the Creed dictates, there must be a better way. This is the Way. After a little side tangent.

Fever dream

I hate being nostalgic, who am I kidding, I love being nostalgic. Wallowing in memories and reminiscences of a better sepia coloured days of the past. So taking you people back to where it began, there once was a day where you just had HTML. With some embedded styling into the HTML tags. That was wonderful but unmaintainable. So CSS came to the rescue and it was a godsend.

It was so awesome to just fire up a text editor and code a website on your own PC! More people should do that these days. Then came some interactivity in the form of JavaScript but it was mainly for the XMLHttpRequest to talk to the backend. Okay, so far so good. Still maintainable by a text editor and just good times and vibes.

Slowly but surely this devolved into what we have today. We have seen the likes of Gulp, Grunt, Webpack, Snowpack, Turbopack, Rollup, Parcel and Vite and countless others to just transpile/bundle a web application. Then of course we have frameworks like Angular (in many incarnations), React, Preact, Svelte, Vue, Nuxt (not to be confused with Next) and you name it.

The same in the world of CSS (Bootstrap, Tailwind, Foundation, Bulma and countless others). Then of course there is Babel, Terser and you have to minify your code, and have polyfills for different dialects. Which leads me to say there are different dialects of JavaScript still to this day. There are ways to compile for Safari or Chrome or a EcmaScript standard.

Can frontend devs really say they have a wonderful time these days? I freaking loathe this new way of working with so many tools. I genuinely think most of the modern SPAs or other applications could have been written in stock vanilla stuff using HTML, JavaScript and some CSS like the olden days.

But then you cannot write tests, and have a transpile step that can fail so there is no way to check if you did it right in the CI/CD right? Right?! Well guess what, I just ran a couple of invocations of the TypeScript React project and despite an error it did not care. I changed document to documen inside the render function of React. It gets transpiled just fine. Even imports that are wrong and have a wrong reference, it does not care.

Snap back to reality....

Second circle of hell cont.

Okay so the Way decrees that we have to improve this. I tried so many things. The problem is that create-react-app uses react-script and that has a deep down implementation of WebPack. You can npm eject it but I was not wanting to do that just yet. Since fixing WebPack configuration is horrible. Although there exists some drop in replacements, so I tried them all. None of them worked close enough that I wanted to pursue that line.

Then I found nx a tool that could do it for me. I ran that, and in our project that let to some horrible broken flows and it genuinely just removed a bunch of stuff that was important and it changed to Vite without proper migration. Okay that is a no-go.

Then I found CRACO which is a way to override the webpack stuff deep in react-script but with a simple config in the root. This looked promising. So I changed into esbuild being the one that ran all the transforms and everything. I also changed the runtime to bun to make it all as fast as possible. Locally the build time dropped from a molasses 7 minutes to 2 minutes. This is on a 16 core, 32 Gb memory laptop. Are you freaking kidding me. This is the best it can do?

It turns out no, if you just run the transpile step with the binary alone it completes in less than 1 second. It produces an accurate binary but without loading from the index.html. I could fix that, but it would be too invasive for this older more fragile and error prone project. If it was more recent I would definitely do it.

Then of course in the CI environment it ran out of memory. Of course. This had partially to do with the build tool itself and the fact there were circular references. Fixed the circular references, still ran out of memory.

Switched to an older NodeJS runtime, it all worked. What?! WHUUUUTT?! Are you saying I have actual proof that we used to build better things? Yes. Yes I do.

So onwards to the next circle of hell.

Third circle of hell

The fact that you have all these different package managers and they all work slightly different. I hate it. I now have used every permutation and none of them are consistent enough that we can support all of them. So yeah, I gave up. Then because of the free package we only had 6000 credits. Those were gone in one week of this refactoring. With no clear nice easy way to manage PR triggers and path based filtering.

I switched to GitHub actions.

Fourth circle of hell

Well maybe 3.5, since it was a remaster of the previous one. The same stuff happened again. I tried to make it work with the newest lerna and so a newer NodeJS runtime was needed. However that broke many different incantations of scripts we had so I debugged that and improved it there. Finally the caching did not work because that particular action does not really work for lerna. So I switched to a correct one. Then the frontend was done. It all worked. It was glorious. I slowly climbed out of this hell.

The backend was easy. It is just Python, poetry and the actions all just worked. It ran in less than a minute which was more than enough for me on the CI.

Hmmz the frontend is still building, oh and it ran out of memory. As I fell slowly back into the shadows contemplating my life's choices and felt hope dwindling I used all my willpower to hedge my bets on this parameter --max-old-space-size=SIZE for NodeJS. It makes it so it can request more memory for the older runtimes. I plugged it in and lo and behold I got expelled from the hell I was in back to the mundane life I was living.

Conclusion

I loathe the frontend. I really do. I cannot stand doing anything in it. However conquering it always feels so sweet. So it is a difficult relationship.

I wish the world of frontend would become more open and easy to access for new people. It was always so much fun back in the day to just start firing up coding and seeing progress. This also happens in almost all other programming languages. Just in the frontend it seems to be so abrasive that it seems it does not want you to even start. Just stay away.