2021 in Review
Warning!
This article may contain outdated information, as it is more than two years old.
As 2021 draws to a close, I suppose it’s time for me to start babbling about what I’ve learned about in web development this year. Every year contains new surprises and new releases of libraries and packages and all sorts of other stuff, so there’s quite a bit to talk about.
(Not a very decent introduction)
Brave Browser #
First of all, I became “braver” and started using Brave Browser! Safari’s privacy safeguards were not that optimal, the extensions weren’t all that available, and many Web APIs behaved in weird ways. Which is not so nice for a web developer. So I switched to Brave, which happens to be based on Chromium, arguably the browser engine that’s most up-to-date and on the cutting edge.
I still use Ghostery, though — it’s a nice complement for blocking trackers that Brave misses.
One annoying little caveat I discovered about Brave, however, is that it blocks all navigator.sendBeacon
requests by default. Which makes sense from a privacy hardliner’s perspective because sendBeacon
is used for logs and analytics, never for serious data exchange (that’s what it was built for, at least). However, from the perspective of a developer who’s trying to collect useful, privacy-respecting analytics, Brave is penalizing the use of modern Web APIs like sendBeacon
and forcing them to use other methods such as XMLHttpRequest
(Plausible) and make-believe images (Fathom). This is most certainly not an optimal situation. I hope that Brave can be more lenient towards privacy-respecting analytics and logging services.
1Password! 🎉🎉🎉 #
Before, I had passwords in a mess of CSV files, Chrome, Brave, and iCloud Keychain. I had to copy and paste passwords from other places when I needed them, and when I had to use passwords in apps I had to go into browser settings to copy the password.
Well, I guess I finally understood why unified password managers have a big market.
So I tried out 1Password, which seemed to be the leader in the industry. Which it was. The UI looked amazing (especially on the beta 1Password 8), the encryption was detailed in a really nice white paper, and the experience on macOS was really smooth.
I’m still in the 90-day trial right now, but there is no doubt that I will pay for 1Password once the trial ends. It’s just so good.
(I am not sponsored 🙃)
Electron #
I went into Electron, because I wanted to build a better-looking and minimalistic Minecraft launcher after I’d seen many launchers which were heavyweight, made with Java, and took ages to start up. I hesitated for a long time because the multi-process model in Electron felt daunting.
Which it was. Fortunately, the Vite Electron Builder starter made the learning process much simpler for me, because I didn’t have to configure all of the npm scripts and other things to get started. It also came with Vite, which is a wonderful build tool.
The Inter-Process Communications (IPC) model was a source of confusion and consternation for me as I ran huge node scripts inside the renderer process and tried to access the DOM from the main process… But after a bit of learning from example and error traces, I got it straight. I’d like to detail the IPC model in a blog post “coming soon”, but for now this diagram would suffice:
I’m working on the Minecraft launcher right now with Electron and Vue, to be released sometime in 2022. ✨
TypeScript #
TypeScript makes coding with JavaScript just so much easier! For those of you who don’t know what TypeScript is, it’s basically an extension of JavaScript that contains types and connects with your IDE via language servers to provide you with better autocomplete and linting.
TypeScript is a really great productivity booster. It’s incredibly useful when you don’t know how to start working with a library, because, well, you can just look at the type of its exports and the types of arguments the functions receive and get started without ever needing to even look at the docs!
TypeScript can also help you find errors in your code. For instance, this instance of JavaScript fails really quietly until you run it:
const thisIsAThing = 1;
console.log(thisIsAThng);
// ^^^^
// spelling mistake
And other more sophisticated mistakes. In contrast, TypeScript shows up with a big error message straight away and also provides useful hints:
const thisIsAThing = 1;
console.log(thisIsAThng);
/*
(error from TypeScript)
Cannot find name 'thisIsAThng'. Did you mean 'thisIsAThing'? ts(8888)
example.ts(1, 7): 'thisIsAThing' is declared here.
*/
It also helps you when you’re expecting some types and want to enforce them: for instance, if you want to have a function return a string, you can enforce it by writing
const prefix = (original: string): string => {
return "[ryanccn.dev] " + original;
};
That way, if you lose track of what your code is doing and write something else that’s not supposed to be there, such as
const prefix = (original: string): string => {
console.log("[ryanccn.dev] " + original);
};
The TypeScript compiler will very promptly throw an error:
A function whose declared type is neither 'void' nor 'any' must return a value. ts(8888)
So you see, that’s incredibly useful.
I’m using TypeScript in all of the projects in which I can, because it’s such a productivity booster and makes for a wonderful IDE experience.
(Unfortunately, Eleventy does not support TypeScript, which is why I’m considering moving this site to Next.js — yet again. 😅)
clank #
This is a project born out of my own convenience and a generally open source spirit. clank is a simple CLI made with Deno (more on that later) that compiles your C++ source file into a custom cache directory before running the executable.
The CLI experience is made with Cliffy, a very useful CLI toolkit for Deno.
Some features include:
- Passthrough
std{in,out,err}
- Prints status code and corresponding signal
- A cache based on unique hashes of files
- Smart deletion of cached files to keep cache size under 0.5 GB
- Configure which compiler to use
- Passing options directly to compilers
- An incredibly convenient upgrade command
- Shell autocompletion support
- Looks great
The gist of the clank
workflow when you run a C++ source file is this:
- It creates a hash of the source file using SHA-256 in the Deno standard library.
- It checks whether this same file has been built before by checking whether the executable file exists in clank’s cache.
- If it doesn’t exist, it compiles the source file using
clang++
org++
(depending on your OS) to a file named with the hash. - If it does, it skips compilation and goes straight to execution.
- If it doesn’t exist, it compiles the source file using
- It executes the executable with
stdin
,stdout
andstderr
all set to'inherit'
so that the experience is just like if you were running it directly. - It exits, but not before it checks if the cache size is above 1GB. If it’s above 1GB, it uses a cool algorithm based on the age of the file and the size of the file to determine which files to delete first.
This project was fun to make and easy to use — if you also do CS competitions in C++, using clank will absolutely speed up your workflow!
This is how to install if you have Deno already installed:
deno install -Af -n "clank" --import-map "https://deno.land/x/clank/import_map.json" https://deno.land/x/clank/mod.ts
Then, just run any C++ file like this:
clank hello.cpp
Deno & Deno Deploy #
Deno is the cool new runtime for JavaScript and TypeScript made by Ryan Dahl, the original creator of Node.js. It’s cool, because it tries to comply completely with the Web APIs commonly used in browsers, and it’s incredibly modern, with top-level await
(which is still not generally available in Node.js) and modern ES syntax built right into it.
It’s fast, because it’s made with Rust (among many other CLIs), and it includes a formatter, linter, compiler, documentation generator, and other tools in one bundle of a CLI.
It uses ES Modules, which is ever so useful, and it imports only via URLs, which relieves you of the burden of having to maintain a package.json
and makes it incredibly easy to test out libraries.
Correspondingly, Deno Deploy is
a distributed system that runs JavaScript, TypeScript, and WebAssembly at the edge, worldwide.
It runs Deno scripts that you can just run with the Deno CLI on your local dev environment, with support for the built in HTTP server modules. It’s a distributed worker architecture, like Cloudflare Workers and Lambda@Edge, so it loads really fast, and it’s really easy to make because everything is based on Web APIs, like fetch
and Request
and Response
.
For instance, this is from the source code of my link shortener at a.ryanccn.dev
:
const handler = async (req: Request) => {
const fragments = new URL(req.url).pathname.split("/").filter((a) => !!a);
if (fragments.length === 0) {
// root url
return responses.redirect("https://ryanccn.dev");
}
if (fragments.length >= 2) {
// more than 1 piece of path
return responses.notFound();
}
if (req.method !== "GET") {
return responses.notAllowed();
}
const res1 = await get(fragments[0]);
if (!res1.ok || !res1.data) {
// no redirect exists for such an id
return responses.notFound();
}
// ok
const res2 = await click(fragments[0], res1.data.clicks);
return responses.redirect(res1.data.t, res1.latency + res2.latency);
};
It looks really simple, and with the flexible ES Modules system built right into Deno, it’s easy to modularize functionality, as you can obviously see from the imports from ./db.ts
(in which I connect to Supabase) and ./responses.ts
(in which I have functions for creating Response
s).
Deno is a promising piece of tooling for the future, and the only problem now is that the ecosystem is not yet complete, so many sophisticated setups required for building big web apps and native apps are not possible. However, building simple scripts with Deno has been a wonderful experience, and I look forward to the day when I can use Deno to build a desktop app or a mobile app or something in IoT. The future is endless! 🚀
Lines of code (tokei) #
These are clips of output in each of my projects from tokei, a CLI (as always, written in Rust) that counts your code really quickly. It deserves a checking out!
redirects #
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
TypeScript 5 175 134 4 37
YAML 1 7 7 0 0
===============================================================================
Total 6 182 141 4 37
===============================================================================
clank #
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
C++ 3 37 26 0 11
JSON 1 6 6 0 0
TypeScript 18 757 593 4 160
-------------------------------------------------------------------------------
Markdown 1 44 0 30 14
|- BASH 1 34 14 11 9
(Total) 78 14 41 23
===============================================================================
Total 23 844 625 34 185
===============================================================================
redirects #
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
TypeScript 5 175 134 4 37
YAML 1 7 7 0 0
===============================================================================
Total 6 182 141 4 37
===============================================================================
ryanccn.dev #
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 2 213 169 11 33
JavaScript 26 1096 899 57 140
JSON 2 94 94 0 0
TypeScript 3 218 169 7 42
-------------------------------------------------------------------------------
HTML 1 19 19 0 0
|- CSS 1 38 34 0 4
(Total) 57 53 0 4
-------------------------------------------------------------------------------
Markdown 12 1173 0 696 477
|- BASH 2 5 5 0 0
|- HTML 2 102 100 0 2
|- JSON 3 91 84 0 7
|- JSX 6 502 387 47 68
|- Markdown 1 0 0 0 0
(Total) 1873 576 743 554
===============================================================================
Total 46 2813 1350 771 692
===============================================================================
Untitled Minecraft launcher (WIP) #
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 1 1 0 0
HTML 2 35 34 0 1
JavaScript 9 310 236 38 36
JSON 5 100 99 0 1
TypeScript 11 518 355 89 74
-------------------------------------------------------------------------------
Vue 1 5 4 0 1
|- HTML 1 19 19 0 0
|- JavaScript 1 53 42 0 11
(Total) 77 65 0 12
===============================================================================
Total 29 969 729 127 113
===============================================================================
C++ #
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
C++ 383 17177 12736 401 4040
===============================================================================
Total 383 17177 12736 401 4040
===============================================================================
[private project] #
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
CSS 1 27 23 0 4
GraphQL 1 35 30 1 4
JavaScript 4 80 72 3 5
JSON 4 70 70 0 0
TSX 9 393 331 0 62
TypeScript 6 246 130 76 40
-------------------------------------------------------------------------------
Markdown 1 31 0 17 14
|- BASH 1 3 2 1 0
(Total) 34 2 18 14
===============================================================================
Total 26 882 656 97 129
===============================================================================
Total #
(this might not be an accurate sum of the previous snippets, but whatever, it’s okay)
But 22,797 lines of code is pretty impressive to me though!
===============================================================================
Language Files Lines Code Comments Blanks
===============================================================================
C++ 386 17214 12762 401 4051
CSS 4 241 193 11 37
GraphQL 1 35 30 1 4
JavaScript 37 1420 1154 97 169
JSON 11 266 265 0 1
TSX 9 393 331 0 62
TypeScript 43 1914 1381 180 353
YAML 1 7 7 0 0
-------------------------------------------------------------------------------
HTML 3 54 53 0 1
|- CSS 1 38 34 0 4
(Total) 92 87 0 5
-------------------------------------------------------------------------------
Markdown 14 1248 0 743 505
|- BASH 4 42 21 12 9
|- HTML 2 102 100 0 2
|- JSON 3 91 84 0 7
|- JSX 6 502 387 47 68
|- Markdown 1 0 0 0 0
(Total) 1985 592 802 591
-------------------------------------------------------------------------------
Vue 1 5 4 0 1
|- HTML 1 19 19 0 0
|- JavaScript 1 53 42 0 11
(Total) 77 65 0 12
===============================================================================
Total 510 22797 16180 1433 5184
===============================================================================
Thanks a lot for reading this article to the end! If you have any thoughts or just anything to talk about, you can DM me on Twitter @RyanCaoDev. Or take a look at my projects on GitHub!
It’s 23:59:59 right now, and with that, wish you all a very happy New Year! 🌈🦄🌈