This is a report on my ongoing field trip to create WebApps.
I have often in the last years thought that it is absolutely ridiculous that as someone making their living writing software, I cannot actually create any useful software by myself: Being able to quickly debug ridiculous problems, perform surgery in large codebases, or navigate corporate social jungles are all helpful skills, but they do not help solve anyone’s problem unless their problem is already software-shaped. These are not the skills that go into creating something useful for actual humans.
Of course, there is also just programming as the act of creating actual software with features. But even there it seems that the range of people that are willing to download and execute a native program (or maybe a DotNet executable) on their computer is a pretty small niche compared to the general population. I am not saying that this niche is not relevant, or that there isn’t tons of great native software, but when your goal is to stop writing tools for other developers, then that’s maybe not where you should go fishing. Even I myself can see that I am less likely to use a program when I first need to download it and it is not running on my phone. In short, when my mother would hypothetically ask me to solve a problem for her using software, the odds that an acceptable solution comes in the form of a C++ program on desktop just seem slim.
So I set out to learn something about “web apps”, which I am just going to call anything that is accessed via a browser. At first, I thought I should maybe take what I know already and apply it:
- I started looking into using WebGL and DearImGui via Emscripten to create WebApps. I never released anything, but I did created maybe 5-10 small things. The main struggles I have found are that basic functionality is incredibly brittle (try getting copy-and-paste to work consistently - good luck!), the end-product feels anything but familiar to users (you can’t select text and right-click it!?), and you import the most annoying parts of native development into your process, like still using CMake. The last part actually got a bit better with vcpkg (there’s a community triple for WASM now), but it’s still just as annoying as with native C++ development. Though that build annoyance is actually the least of my concerns, since users do not care about my build setup. In general, every step of development seemed to be an up-hill struggle, trying to reimplement features of a platform I do not even properly understand just to get parity.
- I repeatedly tried to use C# with Blazor and Razor to create web pages. I got frustrated with it, because many of the parts I wanted to use fell apart when I even just poked at them. For example, stripped AOT builds never worked, and non-stripped AOT builds for an essentially empty Blazor project took more than an hour on my machine. Granted, my machine is more than 10 years old by now, but that was still unexpected (the build exhausted my 16GB of RAM, so I was effectively looking at 1 hour of hard pagefaults).
Ironically, I think that this general approach is one native developers usually despise: we’re ignoring the platform and try to impose a familiar model on it. For better or for worse, the browser is the platform, and the browser exposes everything as potentially async Javascript APIs. When using emscripten with WASM, or C# with whatever, you are making your total tech stack deeper and more complicated. It usually doesn’t take long before you need to interact with something that is not covered by your particular choice of stack and then you start doing Javascript interop. Or the framework (like C# ASP) relies on still using CSS/HMTL/JavaScript, so you have not really saved anything. Or you want to implement link previews (like on Twitter) and realize that this probably requires some server-side rendering, so you are back to HTML. In all of the cases above I spent plenty of time fighting the tools and tech stack solving problems that came from using these tools in the first place. And all that just so I could ignore the platform below. Ultimately it unsurprisingly turns out that you just can’t ignore the platform. Who could only have guessed that! – In short, these are surely useful tools but to make effective use of them you should already have understood the underlying platform, as everywhere else.
Side note: It is not lost on me that C++ and C# do the same thing on the desktop and that it is “turtles all the way down.” Yet I find that I hit the limits much more quickly on the web (for example, the Win32 API is still a C API at least). Additionally, on the desktop it also very clearly pays off to understand not just the layer that you are operating on but also as many of the layers below as you can afford to get familiar with.
With those learnings, I then turned to building things using HTML/CSS/Javascript. Here are three things I built:
- I built a small tool that creates histograms from CSV files. This was actually the outcome of multiple approaches to create an app that just accepts files from the user via drag-and-drop and then does something useful with it.
- I ported my piano practicing software (partially) to the web. It’s not publicly accessible.
- I made a web app to create small “receipts” that you can fill in and then send the filled-in version to other people. Here is one such receipt, and here is a filled in version. On the main page, you can login with Google and create your own receipts.
Here are some things I learned and experienced:
- Things got much smoother once I ditched C++ and C#. I spent less time fighting the technology. It is also clear that most services assume that you are using a JavaScript stack, and this makes your life so much easier. With ASP and C# for example, it always felt like I had to fight the system to install an additional NPM package, whereas in JavaScript that is trivial.
- While JavaScript gets of all the hate on the web, it is actually quite OK for small web things. I have used Typescript a bunch, and it helps as well. But the real enemy is CSS: or maybe not just CSS, but layouting in general. Going from “thing in my mind” to “and here is the CSS for that” still feels like a black box. (More than anything else, I wanted to use DearImGui to solve this problem for me.) I have used Bootstrap as a component library, but I am not really proficient with it. I am also not sure whether it won’t just suffer from the same problem outlined above, where it only really provides its value when you already know the lower layer. I think that investing some time into properly learning CSS is going to pay off hugely.
- I did some work with a more modern JavaScript frontend framework (React) but found that to be overkill for my needs. I might give it another try in the future, but ultimately I am not convinced that I already have the problems those frameworks are solving. The complexity I would get from adding them into my tech stack is not worth it if they just save me from typing out some event subscribers.
- I was surprised to find that JavaScript suffers from a problem I know very well from C++: You will have to bring your own build system, and there is potentially a lot of complexity here. Bundlers, minifiers, tree-shaking, polyfilling, project setup, package management, custom javascript parsers. If you are new to the space and just look for “the standard way” to do something, you are not going to find it. Even things like “how do I start a project” have too many answers to be useful. In the end, I settled on using Vite and npm, but have also tried Parcel and Webpack. Vite somehow stuck as approachable.
- NPM just works, and having a working package manager made at least me much more prone to accrue plenty of dependencies. It took discipline to cut it down again. I was often only using a tiny part of something else, and it was trivial to reimplement just that part. (One case that I did not resolve is that my piano app uses a package for note rendering, and that package embeds megabytes of fonts that I can’t get rid of.)
- The pain involved in just getting the basic pieces together to have an app with a frontend and a backend can vary greatly. Originally, I planned to have my Receipt tool run with a C# backend. It did not seem like something that should require a lot of work, but it turned out to be just enough to convince me to do something else. For example, the web interface for Azure is just intimidating and complicated for beginners. In the end, I ditched my C# backend and used Firebase, which is Google’s friendly version of their probably just as complicated Google Cloud offerings. It probably is a worse choice in the long run, but it was so much easier to get started with.
- The ecosystem is very much framework driven, to the point that you can often only find examples or answers in the context of a framework instead of expressed in the lower level terms of JavaScript and HTML. As a concrete example, I looked into getting Google Authentication to work, with a server backend written in C# and a frontend in JavaScript. ASP already has this built in, but if you are looking for information on how to actually build that yourself, that’s much harder to find.
Many of my learnings above are instances of “this was hard to learn.” I would really value a foundational education in this field, but so far I did not get the impression that it exists: everyone is so busy telling you what services and frameworks to use this month that they forget to tell you about the simple things or how things actually work outside of some framework. I feel like there is a gap between “this is how the internet works” and “this is how you build webapps.” I did go through several courses on the topic, but maybe just the wrong ones. (It still feels like I am skipping the step where I am running my own server to even understand the problems I am avoiding by getting someone else to do that for me, for example.)
In total, my experience so far was mixed but I have a hunch that I have climbed the first few hills now and can see the mountains ahead. I have already reached the point where I can make webapps that solve real problems for real people on real devices. I am planning to continue this exploration.