Kevin Yank

Recent articles in web development (page 1 of 5)

  1. MelbJS March 2023 notes

    Raw notes from the MelbJS meetup held at Culture Amp in Richmond on 8 March, 2023.

    Microfrontends are an architectural pattern to split up large codebases into small apps that are stitched together into a single page at runtime. Problems of a single build (monolith):
    • Scaling build times
    • Dependency versioning: Team A wants to upgrade React, Team B isn’t ready
    • Deployments: slow builds means deploys back up
    • In general, bottlenecks!
    Multiple apps: a new bundle per “page”. New problems:
    • Compromised user experience
    • Hard to maintain client state across apps
    • Scaling challenges still exist within each app
    • Duplicate dependencies
    Webpack externals: shared dependencies are loaded separately. New problems:
    • No on-demand loading of code. All dependencies must be available first.
    • Manual juggling of those dependencies.
    • Inflexible
    Native ES Modules: trust the browser to load your dependencies for you. New problems:
    • Performance: round-tripping of cascading requests for dependencies.
    • Limited client support.
    • Can’t use non-ESM dependencies.
    Module federation: a run-time method of sharing modules between independent builds. Terminology:
    • Host: The first webpack runtime to boot on the client
    • Remote: A bundle that the host can dynamically load in when requested.
    • These are not mutually exclusive.
    Redundancy, self-healing and versioning come built-in. Already-loaded modules get used. Missing modules get auto-loaded. Doesn’t interfere with a familiar developer experience. Works in the browser and in Node. Remote loads are “hoisted”: an entire tree of missing dependencies will be loaded in parallel. Demo: Two independent webpack apps in a monorepo. Both dev servers running. App1 loads App2 as a remote! App2 is lazy-loaded, but it doesn’t have to be. App1’s webpack config has a ModuleFederationPlugin that declares a remote for App2. App2’s webpack config has a ModuleFederationPlugin that declares that App2 is remote that can be loaded by a host. When App1 loads App2, App2 gets its react, etc. from App1. A bundle can be both a host and a remote, which enables some interesting configurations. The module federation project repo has dozens of examples. E.g. Bidirectional, two modules that can each act as a host, but load the other on demand. App Shell, a single host designed to load multiple remotes. MF first shipped with Webpack 5 in Oct 2020. Next 13, SSR support are now there. Delegate modules, a new feature like middleware for loading remotes (e.g. dynamic host selection, etc.), just landed. Further reading:
    • Follow Zack Jackson, @ScriptedAlchemy.
    If JavaScript is a single-threaded language, then how does async code run? Promises are a combination of two specifications: ECMAScript and HTML. The browser APIs are async operations processed by the browser engine, which allows the JavaScript engine to keep processing. The browser maintains an event loop, background micro-task queues, etc. to make this all work. A tour of the relevant specs, using a simple promise example:
    const resolution = 'MelbJS!';
    new Promise(function executor(resolve, reject)
      const resolveIt = true;
      if (resolveIt) {
      } else {
        reject('Ew. JavaScript!');
    .then(function onFulfilled(result) {
    How it works (according to the spec):
    1. Check that the Promise constructor was passed a function as an executor.
    2. Create an internal promise object, using the Promise prototype, which includes all the promise features we’re used to (then, etc.). Creates some internal “slots” for information about the promise.
    3. Create the resolve and reject functions, which are passed into the executor later.
    4. Call the executor function. If it completes successfully, it’s a normal completion. If it throws and exception, it’s an abrupt completion.
    5. Return the promise!
    How the resolving functions (resolve/reject) work internally: Depending which is called, the promise will be fulfilled or rejected. If resolve is called with another promise, that promise is used in turn to continue resolving. Otherwise, the promise is resolved straight away. How a promise is fulfilled:
    • [[PromiseState]]: fulfilled
    • [[PromiseValue]]: 'MelbJS!'
    • [[PromiseIsHandled]]: false
    • [[PromiseFulfillReactions]]: undefined
    • [[PromiseRejectReactions]]: undefined
    Once a promise is fulfilled, it needs to be handled. That’s where we move into the then method. The specification for this function is really, really long! For brevity, we’ll ignore all the parts that have to do with rejecting. This is where the HTML spec starts to come in. The then method creates another promise, which operates just like the one we’ve been talking about. It’s a bit hidden, but it’s what lets us chain promises. then takes a callback, which gets passed to HostMakeJobCallback, and eventually is transformed into a fulfillReaction. Gets enqueued as a promise reaction job, which is sent to the browser engine – which takes it out of the JavaScript single-threaded execution environment! Its execution is controlled by the browser engine, not JavaScript itself. The Event loop processing model:
    • Event loop
    • Task queue
    • Microtask queue
    • JavaScript runtime engine (heap and call stack)
    There are entire talks on this stuff, so we’re only going to cover it briefly. The JavaScript engine adds code to be executed to its call stack, but the browser event loop can add code to it too. The event loop schedules tasks onto the task queue and microtask queue. Every time the event loop “spins”, it checks if a task is still executing in the call stack. If not, it will place a new valid task on the call stack. The microtask queue is a priority queue. Being in the task queue is like boarding a plane in Economy Class. So, back in the world of the promises standard, HostEnqueuePromiseJob schedules tasks on the high-priority microtask queue (defined in the HTML spec). Another example of how these two standards operate together: HostCallJobCallback. Not covered for time (but equally interesting!): stuff like what happens if resolve is passed another promise. Opens up another can of worms. Also interesting, how browser APIs like fetch work under the surface. Pyodide: compile Python to WebAssembly. Cortical Labs: we grow neurons (live brain cells) and program them to play video games like Pong! The goal for us was to abstract away the low-level C that we were writing into a higher-level language like JavaScript or Python. This would enable us to develop APIs to work with the neurons without the need for knowing signal processing, spike detection/sorting, stimulation patterns, rewards, etc. Python was the most popular language when we looked, so that’s where we started! We built a web-stack IDE (Monaco text editor engine). Pyodide JS: Two-way communication between JavaScript and Python runtimes. Use Web Workers to prevent Python code from blocking JavaScript. Convenient type conversions for the primitive types supported by both languages. Load the Pyodide JS bundle, then write a little bootstrap script and pass it Python source code to run. Just like that, Python running in the browser! No backend, no installation. Take the value returned by Pyodide and call its toJS method to convert the return value of the Python program to JavaScript. Can also use pyodide.registerJsModule to pass a JavaScript value into the Python runtime before running the Python program. Demo: a game of Pong implemented in Python, rendering its current state to a Canvas with JS. Pyodide is important because it will bring all of the machine learning and AI technologies (implemented in Python) into the Web, where we can give them a UI.

  2. Fix System Beep on Move Editor into Next Group in VS Code

    In Visual Studio Code, I frequently want to split the window into two panes to view one file alongside another. The quickest way to do this without my hands leaving the keyboard is to use View: Move Editor into Next Group with the keyboard shortcut ^⌘→ (ctrl-cmd-right arrow).

    Screenshot of the command palette in Visual Studio Code with the command highlighted
    You can find the command in the Command Palette
    Although this works out of the box in VS Code, for several years now on macOS it also triggers an annoying system beep. A few years ago this was annoying me enough that I tried to track down the cause. The issue seems to be that Chromium (the browser engine in Electron, the framework that lets you build desktop applications with web technology, with which VS Code is built) does not notify macOS when it handles keyboard shortcuts like ^⌘←, ^⌘↓, and ^⌘→, so macOS plays the system beep to warn you that your keystrokes didn’t do anything (even though they did). A bug report for this on the Chromium project was closed in January 2020 as WontFix, seeming to blame macOS for the issue. Fortunately, there’s a work-around: set up custom macOS key bindings. You can establish system-global key bindings for the key combinations ^⌘←, ^⌘↓, and ^⌘→ that are mapped to no operation ("noop"). Simply having these declared as valid keystrokes at the OS level eliminates the system beep that occurs even when a Chromium app accepts and handles the keystroke. In order to establish this, you need to create a ~/Library/KeyBindings/DefaultKeyBinding.dict file. Note that you’ll probably need to create the directory as well, and that the directory name is plural (Bindings), but the file name is singular (Binding). This should be in your user Library folder, not the /Library folder or the /System/Library folder. This should be a text file with these contents:
      "^@\UF701" = "noop";
      "^@\UF702" = "noop";
      "^@\UF703" = "noop";
    ^ means Ctrl, @ means Command, and \UF701, \UF702, and \UF703 are the codes for the three arrow keys. There’s a nice reference Gist for this file’s syntax here. You can download a copy of this file if that’s easier (but you’ll need to extract it from the ZIP archive): Once you have created this file, restart VS Code (or any other applications where you want to use these keystrokes), or just reboot your system. After that, you should find that the system beeps are gone. I have successfully applied this tweak since macOS Catalina (10.15.2) all the way up to the current macOS Ventura (13.1) to use the default keyboard shortcuts for the View: Move Editor into Next Group and View: Move Editor into Previous Group commands without hearing system beeps each time.

  3. Horizontal Scrolling

    My new site design makes heavy use of horizontal scrolling on the home page, with scrollbars hidden. Horizontal scrolling can be accessible, but it takes some work to make it so. This article covers accessibility for keyboard and screen reader users, and users whose pointing devices simply don't support horizontal scrolling! It also breaks down several usability features I added to create a better experience than the native scrollbars they replace.

  4. Web Directions Hover 2022 Day 2 notes

    These are the live notes I took from Day 2 of the Web Directions Hover 2022 conference for our team internally at Culture Amp.

  5. Web Directions Hover 2022 Day 1 notes

    These are the live notes I took from Day 1 of the Web Directions Hover 2022 conference for our team internally at Culture Amp.