Thoughts from a Rustacean learning React
At Source.ag we have something called Personal Exploration Time (or PET days for short). The idea is that you get some time, usually one day, to explore a topic that you find interesting which may or may not be useful for Source.ag in the long run. The idea is to give new and innovative ideas a bit of room to breathe before they have to prove themselves. It's explicitly part of the concept that it's okay for them to fail, or conclude that it's not that useful. I love these days, because not only can (and have) they lead to new innovative technologies and ideas being used by the company, but they are also great for my motivation. They help break up slumps when I feel stuck, give me time to investigate technologies I'm really excited about, and just generally let me engage with the creative side of my job.
Just to give you an idea, here are some projects I've done in the past:
- Checking out a new data frame library
- Checking out a new property-based testing framework
- Informally indexing the company's yearly CO2e emissions
- Making a Python & Rust MVP setup
- Optimising some decision rules in our pipeline with bayesian reasoning.
Because so many people took time off over Christmas and we were still busy finalising the roadmap for Q1, rather than have us start on something and put it down immediately again for the holidays, management decided to give the entire cultivate team an extravagant 3-day long stretch of PET at the end of 2022. This did amazing things for the team's motivation (or at least for mine), but that's not what I'm here to talk about today. I wanna talk about what I did for those 3 days: Learning React.
Why React?
Web and front-end work are subjects I've danced around for a long time. I've always gravitated towards backend and analysis work. On the front end side of things, I don't know much beyond the HTML and CSS I needed to make the website you're looking at right now. I've tried to learn javascript and its myriad of frameworks before but somehow they never really clicked for me. In addition, design is not something I feel comfortable with either. In code, I don't usually suffer from blank-page-paralysis, but in design and UI work it feels very overwhelming for me to even get started.
However, I've felt held back by my inability to make simple UIs for the projects and prototypes that I've made. I can make a CLI with no problem, but those have two very major drawbacks.
First of all, they are just not well suited to every program. I think that CLIs work well if you keep them somewhat contained, but some programs need to facilitate so many different interactions that it's not really feasible to do it all ergonomically.
Secondly, when you're trying to demo a prototype I think it's hard to overstate the value of a good visual component (pun intended). Even from my own experience, it's very hard to get a sense of how nice a CLI is without actually using it. For GUIs, however, it's different. Even if it's not the same as direct experience, with a GUI you can at least get across a decent idea by video, and I've found that to be an incredible tool when I'm trying to show off an idea to someone.
Not being able to make a UI makes iterating on the (technical) design a lot harder at times. I like to work iteratively, holistically if at all possible, and to do that you need something that will at least resemble what it will look like to the end user. This is why I think it's really important to dogfood what you build. Backend systems should be in service of the UX, and all too often it's the other way around in my opinion. Even CLIs have a UX even though we don't usually think of it as being such (and that's a problem). This is often why I dislike the old UNIX tools like cat
, awk
, find
and sed
even though they work fine and prefer to use modern CLI tools written in Rust whenever possible; their CLIs are just so much more ergonomic in my opinion.
So those are some of the reasons I wanted to get some basic proficiency in building GUIs. But why React specifically? Two reasons. First of all, it's one of the more foundational architectures for UIs as far as I can tell. Almost every new UI library I see these days touts being "like react", as well as being the topic du jour on the internet when the front end is mentioned. So I figured that even if I didn't end up liking React itself enough to use, getting a basic understanding of how React works would help me get familiar with other UI frameworks. Secondly, and perhaps even more importantly, it's what Source.ag and by extension my colleagues use, so I knew I'd have people to go to for help if I needed it. And boy did I end up needing that.
The project I did was a joint one with my wonderful colleague Neha Kalia. We both wanted to learn a new technology so we decided to team up and build a joke app together. She would take care of the backend with Golang, and I'd get a front end working with React. The extra bonus for me was that Neha also works on our front end and thus is already familiar with React so I could come crying to her whenever things went pear-shaped. And let me just say she was an invaluable resource and an incredible mentor for an FE newbie like myself. Seriously Neha, thanks, you were a lifesaver.
The project pitch
What app did we make, you ask? Well, we built a revolutionary system called "What would my Project Manager say?". I think I'll let it speak for itself:
Is your project manager too busy to talk to you right now? Do you have a burning question about what you're supposed to be doing? Then look no further! What would my PM say? (WWMPS) is a state-of-the-art AI system that has learnt from 1000s of project managers throughout time and across the globe. It has distilled and refined all this knowledge and is ready to dispense these life-changing and invaluable insights to you, yes you, at a moment's notice.
At the risk of ruining it by over-explaining: this is a joke. WWMPS is basically a glorified magic 8 ball that will respond to any question you ask it with vaguely PM-sounding snippets like "Have you made a ticket for that?" or "How does this impact the customer?". And before you ask, yes, I did show it to my actual PM, and she was a real champ about it (Paula, if you're reading this, thanks for taking the joke so well, you're amazing <3).
If you want to check out the final product yourself you can find the code here. At the time of writing it doesn't work because the backend doesn't come packaged with it, but I'll try and make a dummy version available that actually does something.
This was a really fun project, and as always, I have thoughts and opinions. Lots of them. So I thought it would be fun to talk about some thoughts I had while learning JS and React. The timeline of the project looked vaguely like this:
- Day 1: Learn the basics of React by following a tutorial
- Day 2: get some interactions working with dummy data
- Day 3: integrate with Neha's backend
One final disclaimer before we get into it properly: as the title says, these are my thoughts and opinions. This post is not at all meant to be factual or objective. To quote the wonderful YouTuber:
throughout the course of this [blog] I'm going to say some mean things. I'm going to say some nice things! but most importantly I'm going to be saying some mean things. sorry I mean fair, I'm going to be fair.
So I will be saying mean things throughout this blog but I don't want you to come away thinking that I didn't like JS or that I think JS should become more like Rust. I actually liked it much more than I expected to but some things that just boggle my mind and I do think they are worth pointing out. I tend to be harshest on things I actually like because I want them to be the best they could be! If I didn't like the experience, I wouldn't be writing this post. With that out of the way, let's get started!
Initial overall thoughts
I'll be honest, I enjoyed it way more than I had expected. Because I'd tried JS and React a couple of times before and just bounced straight off I was actually a little nervous before starting. Luckily, I started having a lot of fun with it quite quickly. After it all started to click for me after a few hours, I found the (for me) novel structure and idioms much more refreshing than confusing or frustrating, as I had expected. It also helped that Neha and I had a similar style of humour so I could put lots of stupid jokes in there, which was also super motivating for me.
Another advantage I had was that Neha was building a backend alongside me. Having a backend to integrate with made me get familiar with some problems that I otherwise would not have stumbled over like CORS issues and POST requests. (I'm a web newbie okay, cut me some slack.)
It also gave me a better understanding of what the FE collaboration workflow looks like, and how dependent FE folks are on backend stuff. If stuff doesn't work you sometimes have to sit there twiddling your thumbs for a while, and I think it's good to have been on the other end of that for once. There was also some nice back and forth between Neha and me, and I think this ultimately led to nicer designs for both the front end and the back end. Overall, if you ever do a project like this, and you don't necessarily need to learn about backend technologies, I'd still recommend adding a backend to it. It will make the learning experience much more complete and useful even if it's technically less focused.
So overall: both the project and React get a thumbs up from me 👍
The out-of-the-box experience
One thing that I actually didn't like about React was npm
. Coming from Rust, there were definitely a few things that stood out to me.
The first thing is that npm
, or at least create-react-app
(CRA) actively teaches you not to read its output. Any time you run something like npx create-react-app
or npm install
it produces one line of text that it continually overwrites, making it impossible to read. This might seem inconsequential but it's quite frustrating if something goes wrong because now you have no record of what it was actually doing that led to the problem.
Secondly, when I ran CRA for the first time it immediately slapped me around the head with this warning:
npm WARN deprecated tar@2.2.2: This version of tar is no longer supported, and will not receive security updates. Please upgrade asap.
.... Uhm..... am I supposed to do something with that? After a few minutes of running we also get:
added 1397 packages in 1m
214 packages are looking for funding
run `npm fund` for details
Ah, so I've not even done anything and you've already downloaded half the internet on my laptop and are also asking for money? Gotcha. This is a nitpick for sure since other package managers do mostly the same, they just don't tell you about it. I also don't mind when people ask for money so it's not that this is inherently bad, but it is a little weird to do on first contact. At least buy me a drink first, okay?
After some more waiting we get this:
10 high severity vulnerabilities
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.
That sounds serious. Did I do something wrong? I only followed the standard instructions so I guess not. I wanna be a good little developer though, so I better try to fix that. Okay, let's check what this audit thing has to say.
nth-check <2.0.1
Severity: high
Inefficient Regular Expression Complexity in nth-check - https://github.com/advisories/GHSA-rp65-9cf3-cjxr
fix available via `npm audit fix --force`
Will install react-scripts@4.0.3, which is a breaking change
node_modules/svgo/node_modules/nth-check
css-select <=3.1.0
Depends on vulnerable versions of nth-check
node_modules/svgo/node_modules/css-select
svgo 1.0.0 - 1.3.2
Depends on vulnerable versions of css-select
node_modules/svgo
@svgr/plugin-svgo <=5.5.0
Depends on vulnerable versions of svgo
node_modules/@svgr/plugin-svgo
@svgr/webpack 4.0.0 - 5.5.0
Depends on vulnerable versions of @svgr/plugin-svgo
node_modules/@svgr/webpack
Uhmmm, what does that even mean? This gives me no useful information. Okay, let's try applying the fixes. npm audit fix
didn't seem to do anything though... Okay well, I guess I'll try the --force
option then? No idea what's gonna happen but we'll give it a shot and see where we land.
npx audit fix --force
npm WARN using --force Recommended protections disabled.
Huh?! I thought you told me to do this?! What the heck is going on here?! And to top it all off we end with this message:
39 vulnerabilities (2 moderate, 29 high, 8 critical)
What?! So in addition to making me stressed and confused you also only made the issue worse?! This is something I'd expect from a personal project, not industry-standard software. Some googling later I found out the solution is "don't even worry about it, it's fine, trust me." Okay. sure. got it. Delete the now broken project and start from scratch. This time I'm just gonna skip over all the warnings. My conclusion: don't listen to npm
, it only makes things worse. I don't understand why this is considered an okay state of affairs. It's not a good look fam.
The dev experience
After the tussle with npm and finally getting the project set up, it was time to get started. What I usually do is get the bare minimum information about how the language works and then just dive into a project, researching relevant topics as they come up. This is what I originally did way back when I tried to learn React for the first time and.... it did not work, but let me get back to that later.
Luckily this time around I had Neha to help me. She provided me with a really good tutorial. It took me through all the required steps and major mechanics that I initially just typed along with. It took me through all the major mechanics and idioms I'd need to get up and running. Neha also provided me with moral support and helped me get to grips with the counterintuitive stuff when I was stuck.
I must say, by the end of this, I actually feel somewhat competent with React. At least enough to understand basic React code and build basic interfaces for my prototypes, so in that regard, task accomplished! While the npm
bit was not great, I must say that React itself is actually fairly beginner friendly once you get over the initial hump of understanding things like useState
and useEffect
. Credit where credit is due.
Let's come back to how I learn languages for a minute. The bit that I now realise this process relies on is for the language and the tools to actually tell you where you did something wrong and what you need to research to fix it. Sadly... this does not work in JS. The reason for this is basically exemplified by one of my favourite ancient memes:
When you make a mistake JS is very happy to just keep going even if it makes no sense to do so. This means that I have to figure out on my own what I even need to research to figure out what I did wrong.This also brings me to the next point: debugging. it's terrible. At least in my opinion. For example, just to see if I understood how things worked, I wanted to make a button that would only show questions with an even number of upvotes. So I started out with the following code (only the relevant bits):
const [evenUpvotePostsOnly, setEvenUpvotePostsOnly] = useState(false);
const toggleEvenUpvotePostsOnly = () =>
setEvenUpvotePostsOnly(!evenUpvotePostsOnly);
return (
historicalQuestions.filter((resp) => {
if (toggleEvenUpvotePostsOnly === true) {
return resp.votes % 2 === 0;
} else {
return true;
}
})
);
Can you spot the mistake? I tried to assert whether the function was equal to true
not the variable. My understanding was that this was exactly what the ===
as opposed to ==
was for because the former wouldn't do type conversion for you, but it was very happy to carry on with this code which then always returned true without any warning. This took me an hour and two trips to Neha to figure out. This was a bad experience for me.
I've been told that this is partially what TypeScript was invented to solve, but still. If TS is anything like MyPy it does help, but doesn't solve the issue in my opinion. I find issues like these are incredibly frustrating. The fact that nothing told me that anything could even be wrong made it so much harder for me to even figure out where I needed to search for a fault. This was about the moment that I started to understand that I really couldn't just rely on compilers or runtimes to help me figure these things out and I needed to rely much more on forums, blog posts and tutorials. In hindsight, this seems obvious but it still made me a little sad. The help you get from the compiler and other tools is actually what draws me to Rust in the first place, so losing that felt quite significant to me.
Another thing that didn't help was that the control flow in React is very non-obvious to me in the beginning. After having worked with it a bit more the useState
and useEffect
control flow is a bit more intuitive to me now but it took quite a while to figure out and didn't help when trying to debug. This meant that debugging a fairly straight-forward piece of code felt like print line debugging a huge mess of spaghetti code to me, which as you might imagine, is not a very nice experience.
It wasn't all bad though. One thing I liked a lot more than I expected in React is the hot reloading. While the installing etc from npm
feels much slower than it does in Rust, the edit-compile-test loop is much faster, due in part to the hot reload. It even maintains state between hot reloads!
This does occasionally lead to your application getting into a state you've just made unreachable due to a code change, which it can't handle very well. This can be a bit of a brain bender, but on the whole, it made for a very fluid feeling workflow. While I don't think it's something I'll miss in Rust, I was surprised by how much I liked it here, especially for something so visual and interactive.
As a brief aside, I've been trying to decide on a front end to add to the tech stack for a personal project that I'm planning. Initially, my stance has always been "I don't want to learn <insert whatever is hot now> I want to write Rust, that's what works well for me and where I want to build my proficiency". I think that this was partially because I've never liked working with FFIs if at all possible. They have just never been a smooth experience for me, so I wanted to see if I could keep my potential project monolingual.
However, after this project, I no longer believe this is the correct way to go. I've followed the GUI development progression of Rust for a few years now and while I'm impressed with what they managed to get done, I think JS simply just does this better and probably will for the foreseeable future. Especially separating them by a CRUD web API makes the experience much smoother than I expected. Something I did not expect to come away with. Another point in React's favour.
Information availability & legacy software
This is one of the biggest surprises to me. If you're like me, you probably had quite a visceral reaction to reading the words "legacy software". In the mind of most devs legacy software is something terrible. I think this meme sums it up pretty nicely:
However during this project literally the last thing I ever expected happened: I gained some understanding and empathy for the existence of legacy software (or at least backwards compatible software). Not all legacy software mind you, (looking at you, government) but some of it at least. Before this project my opinion on software sounded something like "come on, people should just stay up to date with stuff. Don't you want your stuff to work properly?" I always bemoaned the need for backwards compatibility and openly wondered why people just refused to upgrade and maintain their stack.
This project, however, has made me realise that the breakneck speed with which this JS is evolving and new frameworks are popping up makes the field incredibly confusing for newcomers. What's the difference between Vanilla JS, TS and JSX? What's the difference between React and Angular? Is Vanilla JS even useful to learn at this point? What's ES6? Is that a library or a linter? What's webpack, What's Babel? Why is the answer to every question "who cares, just use the default". This "every decision up front" mentality you get confronted with is one of the things that made me bounce off JS the first couple of times. It's incredibly paralysing if you're on your own and don't know that half the choices you're presented with aren't actual choices.
Secondly, it's incredibly frustrating to find help with things when things become obsolete this fast. Anything to do with React that's older than 2 years is basically worthless at this point, which let me tell you, is most of the stuff out there. React now looks very different syntactically to React a couple of years ago even if they largely do the same. This is incredibly confusing when you're trying to cross-reference things as a beginner. Did this not work because I did something wrong? or did the standard change in the meantime? This StackOverflow question is exactly what I needed, but it's from 2019, is it still valid? Even if the documentations are still correct, having those additional questions associated with everything makes it effectively worthless to a beginner. Thankfully I had Neha to bail me out when I needed it, but without her, I'm not sure I'd have made it to the end of this project.
Now I don't mean to imply that it's that binary. While writing this I am also reminded of the Python 2 debacle and the very "maybe kind of sometimes ever consider please thinking about upgrading some day maybe please?" approach that the python team took. Of course, it's easy to commentate from the sideline, but I can't help but think that if they had been willing to force a clean break a bit harder the segmentation might have been less egregious.
So it's definitely not as easy as "backwards compatible is always better". Sometimes earlier designs can really hamper you in what you can achieve, and I am incredibly impressed with how innovative and backwards-compatible Rust manages to be to this day. I guess there is no easy answer here as there never is. I've just gained a bit more of an understanding of the other side.
Being able to use existing knowledge and interface with existing infrastructure and tools is such a productivity life saver it's not even funny, because you're not just learning them in isolation but also in how they relate to the other bits you're using. Being able to take that more "one step at a time" like you can do if things are more backwards compatible is much more valuable than I first realised. An unexpected, but very useful lesson.
I am definitely going to try and keep my JS skills a bit up to date and also explore other frameworks again to see how the experience compares. Next port of call is probably Svelte but we'll have to see when I have time and energy to dive into something new. Hopefully that will be before the 2023 PET extravaganza.