<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Samuel Rouse</title>
    <description>The latest articles on Forem by Samuel Rouse (@oculus42).</description>
    <link>https://forem.com/oculus42</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F860505%2F9e6f387c-e01d-4a99-ae73-eb7146bc373d.png</url>
      <title>Forem: Samuel Rouse</title>
      <link>https://forem.com/oculus42</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/oculus42"/>
    <language>en</language>
    <item>
      <title>Chaos Proxy: JavaScript Shenanigans</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Sat, 27 Dec 2025 20:34:04 +0000</pubDate>
      <link>https://forem.com/oculus42/chaos-proxy-javascript-shenanigans-4e5e</link>
      <guid>https://forem.com/oculus42/chaos-proxy-javascript-shenanigans-4e5e</guid>
      <description>&lt;p&gt;JavaScript Proxies can do almost anything. To demonstrate, let's set up a simple proxy that will sometimes return incorrect values for a proxied function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Accepts a &lt;code&gt;target&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;Accepts &lt;code&gt;odds&lt;/code&gt; of the error (as a fraction of 1)&lt;/li&gt;
&lt;li&gt;Accepts &lt;code&gt;alternative&lt;/code&gt; function to execute instead of the normal response.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Nothing special here. We're just &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/apply" rel="noopener noreferrer"&gt;using the &lt;code&gt;apply&lt;/code&gt; trap&lt;/a&gt; for functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chaosProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;odds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alternative&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;odds&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;alternative&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This basic implementation doesn't have much to it. Simple odds calculation, and you execute one function or the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;circleArea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;circleAreaClose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;3.14&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roughArea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chaosProxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;circleArea&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;circleAreaClose&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;roughArea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 12.566370614359172&lt;/span&gt;
&lt;span class="nf"&gt;roughArea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 12.56&lt;/span&gt;
&lt;span class="nf"&gt;roughArea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 12.56&lt;/span&gt;
&lt;span class="nf"&gt;roughArea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 12.56&lt;/span&gt;
&lt;span class="nf"&gt;roughArea&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 12.566370614359172&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Variations
&lt;/h2&gt;

&lt;p&gt;I'm not going to build these; I leave them as exercises for the reader. Maybe one of these will speak to you and you'll try solving it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supporting multiple alternatives with different weights/probabilities.&lt;/li&gt;
&lt;li&gt;Support manipulating async/promise values.&lt;/li&gt;
&lt;li&gt;Modify arguments objects to see if mutations cause problems in your app.&lt;/li&gt;
&lt;li&gt;Return a consistent sequence of responses rather than random ones.&lt;/li&gt;
&lt;li&gt;Introduce random delays in asynchronous responses rather than changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Sure, there are other ways to go about this, but awareness of different parts of JavaScript can come in handy one day. Don't forget to spend a little time trying things. It can sharpen your skills, broaden your perspective, and help you solve real problems in the future. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Git Bits: Symbols</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Tue, 21 Oct 2025 12:59:39 +0000</pubDate>
      <link>https://forem.com/oculus42/git-bits-symbols-15ck</link>
      <guid>https://forem.com/oculus42/git-bits-symbols-15ck</guid>
      <description>&lt;p&gt;Git is...complicated. This series focuses on breaking git down into commit-sized pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Symbols
&lt;/h2&gt;

&lt;p&gt;Some of the confusion around git comes from the different special symbols and references used to navigate through repository history, compare changes between commits, and manipulate branches. These may look cryptic at first, but learning what and how they work can change the way you think about git.&lt;/p&gt;

&lt;h2&gt;
  
  
  HEAD
&lt;/h2&gt;

&lt;p&gt;Not quite a symbol, but an important foundation. This is the "You are here" sign on the map that is your git graph. &lt;a href="https://git-scm.com/book/ms/v2/Git-Internals-Git-References#ref_the_ref" rel="noopener noreferrer"&gt;HEAD&lt;/a&gt; is a reference to the current commit you're working on.&lt;/p&gt;

&lt;p&gt;While each branch has a "head" (lowercase) that refers to the latest commit on that branch, the HEAD (uppercase) refers to where you are currently working. While it is most often pointed at a branch, it can point to a tag or a commit as well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HEAD&lt;/code&gt;: The current commit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD^&lt;/code&gt;: The parent of the current commit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD@{1}&lt;/code&gt;: The previous value of HEAD (where HEAD was before the last command that moved it)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ancestry References
&lt;/h2&gt;

&lt;p&gt;Needing to traverse &lt;a href="https://dev.to/oculus42/git-bits-the-graph-2cf9"&gt;the graph&lt;/a&gt; happens. Perhaps you need to quickly see if one of your recent changes caused a defect, or you realize you meant to split off a related feature branch three commits ago. These operators allow you to walk the graph.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tilde (~) Operator
&lt;/h3&gt;

&lt;p&gt;The tilde operator allows you to reference previous commits – or &lt;em&gt;ancestors&lt;/em&gt; – relative to a starting point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HEAD~1&lt;/code&gt;: Refers to the commit one step before &lt;code&gt;HEAD&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main~3&lt;/code&gt;: References the commit three steps before the tip of the &lt;code&gt;main&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD~&lt;/code&gt;: Shorthand for &lt;code&gt;HEAD~1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Combine the last 3 commits into one&lt;/span&gt;
git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; HEAD~3
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Combined commit message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Branch Prediction
&lt;/h3&gt;

&lt;p&gt;When using the tilde operator, you may run into merge commits. Git follows the first parent in these cases. This is particularly useful when you want to go back in history in a linear fashion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Show what was committed one commit ago&lt;/span&gt;
git show HEAD~1

&lt;span class="c"&gt;# Check out the version from three commits before&lt;/span&gt;
git checkout HEAD~3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To help understand, the first parent was the checked out or &lt;em&gt;current&lt;/em&gt; branch when the merge happened. Looking back at a merge, you might think of the current branch as the merge &lt;em&gt;destination&lt;/em&gt; or &lt;em&gt;receiving&lt;/em&gt; branch. When resolving merge conflicts, you may see the terminology "ours" vs. "theirs".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Checkout "our" branch&lt;/span&gt;
git checkout main

&lt;span class="c"&gt;# Merge "their" branch into "our" branch&lt;/span&gt;
git merge develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Our" branch is the first parent in a merge; the checked out branch.  "Theirs" is the branch being added.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Caret (^) Operator
&lt;/h3&gt;

&lt;p&gt;The caret operator also references ancestors but is focused on navigating merge commits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;HEAD^&lt;/code&gt;: Refers to the first parent of HEAD (same as HEAD~)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD^2&lt;/code&gt;: Refers to the second parent of HEAD (only meaningful for merge commits)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main^^&lt;/code&gt;: References the grandparent of the main branch &lt;em&gt;if the last two commits were merges&lt;/em&gt; (same effect as main~2)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Show the first parent of a merge commit&lt;/span&gt;
git show HEAD^1

&lt;span class="c"&gt;# Show the second parent of a merge commit (the branch that was merged in)&lt;/span&gt;
git show HEAD^2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are troubleshooting a defect and want to see the state of a feature branch before it was merged into &lt;code&gt;main&lt;/code&gt;, you might use &lt;code&gt;^2&lt;/code&gt; to navigate onto "their" branch without needing to know the branch name or details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stacking Symbols
&lt;/h2&gt;

&lt;p&gt;These symbols &lt;em&gt;describe a path&lt;/em&gt; through the graph to a particular point. What's more interesting: you aren't limited to one. You can combine a series of traversal operators to get to a specific commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Go back one commit: ~1&lt;/span&gt;
&lt;span class="c"&gt;# 2. Go back on a merge to the incoming or "their" branch: ^2&lt;/span&gt;
&lt;span class="c"&gt;# 3. Go back three more commits on that branch: ~3&lt;/span&gt;
git checkout HEAD~1^2~3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You won't use this style very often, but I think knowing about it helps understand what's happening. These symbols aren't &lt;em&gt;references&lt;/em&gt; to specific things; they are &lt;em&gt;instructions&lt;/em&gt; to navigate the history graph. That's why they are called &lt;em&gt;operators&lt;/em&gt;: unlike commits which are a &lt;em&gt;state&lt;/em&gt;, these represent an &lt;em&gt;action&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;If you look at the history like a map, you might read &lt;code&gt;HEAD~1^2~3&lt;/code&gt; like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Starting at your current location (&lt;code&gt;HEAD&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Go back one intersection (commit).&lt;/li&gt;
&lt;li&gt;Take the other road that merged into this one.&lt;/li&gt;
&lt;li&gt;Go straight three more times.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yc92b5mvrmnwj4myaad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0yc92b5mvrmnwj4myaad.png" alt="Mermaid git graph with arrows describing the multiple traversal steps" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Commit-ish
&lt;/h2&gt;

&lt;p&gt;Not only can you perform these traversal actions from special references like &lt;code&gt;HEAD&lt;/code&gt; and branches, you can perform them from any commit or &lt;a href="https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-commit-ishalsocommittish" rel="noopener noreferrer"&gt;commit-ish&lt;/a&gt; reference like a tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Two steps back from a specific commit&lt;/span&gt;
git checkout 0269f84~2

&lt;span class="c"&gt;# Get "their" branch from a merge commit tagged as v2.1.0&lt;/span&gt;
git checkout v2.1.0^2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Range References
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Double Dot Notation (..)
&lt;/h3&gt;

&lt;p&gt;The double dot notation helps you see differences between two references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main..develop&lt;/code&gt;: Shows commits that are in develop but not in main&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD..origin/main&lt;/code&gt;: Shows what commits will be pulled from origin/main&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the most commonly used notations for comparing branches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List commits in develop that aren't yet in main&lt;/span&gt;
git log main..develop

&lt;span class="c"&gt;# See what changes will be pushed to origin&lt;/span&gt;
git diff HEAD..origin/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use this style to output a patch file for sharing with others:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff HEAD..origin/main &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; activeWork.patch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Triple Dot Notation (...)
&lt;/h3&gt;

&lt;p&gt;The triple dot notation shows commits that are in either reference but not in both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main...develop&lt;/code&gt;: Shows commits unique to both main and develop&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD...FETCH_HEAD&lt;/code&gt;: Shows changes between local branch and what was just fetched&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful for seeing the complete picture of how two branches diverged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See all commits that are in main or develop, but not in both&lt;/span&gt;
git log main...develop

&lt;span class="c"&gt;# Visualize the divergence between branches&lt;/span&gt;
git log &lt;span class="nt"&gt;--graph&lt;/span&gt; &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt; main...develop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The @ Syntax
&lt;/h2&gt;

&lt;p&gt;The @ syntax provides a way to reference points in the &lt;a href="https://git-scm.com/docs/git-reflog" rel="noopener noreferrer"&gt;reflog&lt;/a&gt;. Unlike the regular &lt;code&gt;git log&lt;/code&gt;, &lt;code&gt;git reflog&lt;/code&gt; keeps track of activity on your particular clone of the repo, including branch switches. Navigating through this will be pretty rare, but can be useful for seeing what you've been up to.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main@{yesterday}&lt;/code&gt;: Where the tip of main was yesterday&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD@{2}&lt;/code&gt;: Where HEAD was two moves ago&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HEAD@{upstream}&lt;/code&gt; or &lt;code&gt;@{u}&lt;/code&gt;: The upstream branch of the current branch
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See what the branch looked like yesterday&lt;/span&gt;
git show main@&lt;span class="o"&gt;{&lt;/span&gt;yesterday&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Compare with what you had yesterday&lt;/span&gt;
git diff main@&lt;span class="o"&gt;{&lt;/span&gt;yesterday&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Find commit where you accidentally removed code (from reflog)&lt;/span&gt;
git log &lt;span class="nt"&gt;-p&lt;/span&gt; HEAD@&lt;span class="o"&gt;{&lt;/span&gt;2&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Commit Ranges for Operations
&lt;/h2&gt;

&lt;p&gt;These symbols aren't just for viewing history; they're also used for operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git rebase -i HEAD~3&lt;/code&gt;: Interactively rebase from three commits back&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git cherry-pick main..feature&lt;/code&gt;: Apply all commits from main to feature onto the current branch&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git revert HEAD~5..HEAD~2&lt;/code&gt;: Revert a range of commits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When performing operations with ranges, you can use &lt;code&gt;git log --oneline&lt;/code&gt; with your range to verify which commits will be affected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Git's special symbols and revision references let you control how you navigate your repository's history. The syntax might seem arcane at first, but the operators let you navigate, compare, and manipulate your history with precision.&lt;/p&gt;

&lt;p&gt;Whether you're analyzing branches, cherry-picking changes, or fixing mistakes in your commits, knowing the symbols puts you in charge of your version control tools.&lt;/p&gt;




&lt;p&gt;Author's note: The first draft of this article was produced with the assistance of generative AI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s1"&gt;'Symbols'&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Reactive Data With JavaScript Proxies</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Tue, 30 Sep 2025 15:58:29 +0000</pubDate>
      <link>https://forem.com/oculus42/reactive-data-with-javascript-proxies-30l9</link>
      <guid>https://forem.com/oculus42/reactive-data-with-javascript-proxies-30l9</guid>
      <description>&lt;p&gt;&lt;em&gt;Stale data&lt;/em&gt; is one of the major headaches in modern web development. Especially in React, where hooks require you to manage dependencies manually. Other libraries adopted a variety of techniques to do this automatically.&lt;/p&gt;

&lt;p&gt;Libraries that automatically update values when their dependencies change are often referred to as &lt;em&gt;reactive&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="noopener noreferrer"&gt;JavaScript Proxies&lt;/a&gt; provide a lot of interesting capabilities, so today let's take a look at one way we might leverage Proxies to identify and maintain reactive data. We aren't building out an entire framework, but digging into things out ourselves can help us better understand the language and tools we use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope
&lt;/h2&gt;

&lt;p&gt;I want to walk through building a basic data layer with Proxies. I encourage you to take that base and make changes beyond the scope of the article. I use &lt;a href="https://runjs.app" rel="noopener noreferrer"&gt;RunJS&lt;/a&gt; to write and test the code in this article. It can be a great tool for trying out coding concepts like this, and I recommend it if you want to follow along with each step and try out the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Foundation
&lt;/h2&gt;

&lt;p&gt;I'm starting &lt;em&gt;very roughly&lt;/em&gt; from a concept I liked from the moment I first saw the Vue library: &lt;a href="https://vuejs.org/guide/essentials/computed" rel="noopener noreferrer"&gt;computed properties&lt;/a&gt;. Providing a clear storage distinction between &lt;em&gt;static&lt;/em&gt; and &lt;em&gt;computed&lt;/em&gt; data, and handling caching and invalidation of these computed values was something that interested me, especially because the consumer doesn't need to know that one is a property and one is a function under the hood.&lt;/p&gt;

&lt;p&gt;Proxies are a great tool for this; they can abstract away some of that complexity in the data layer. In fact, &lt;a href="https://vuejs.org/guide/essentials/reactivity-fundamentals.html#reactive-proxy-vs-original" rel="noopener noreferrer"&gt;Vue 3 uses proxies for reactivity&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm not just copying from Vue's design, though. I want to make something of my own to try to understand what is necessary.&lt;/p&gt;

&lt;p&gt;Let's dig into some requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a Proxy object.&lt;/li&gt;
&lt;li&gt;When properties are set, separate functions into "computed"  and everything else into "static"&lt;/li&gt;
&lt;li&gt;Return values when properties are accessed, executing the functions in "computed" through normal property access (no user function calls).&lt;/li&gt;
&lt;li&gt;Allow computed functions access to the proxy object so it can consume other values on the object.&lt;/li&gt;
&lt;li&gt;Cache function calls so we don't repeat computed work if not necessary.&lt;/li&gt;
&lt;li&gt;Track dependencies accessed by computed properties from the proxy.&lt;/li&gt;
&lt;li&gt;When properties are updated, use the dependencies information to clear any cached values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are more requirements necessary to create a robust, production-ready system, but this is a good starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Underneath
&lt;/h2&gt;

&lt;p&gt;From our requirements, we can gather that we'll need some objects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;static&lt;/code&gt; - Static values&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;computed&lt;/code&gt; - Functions for computed properties&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cache&lt;/code&gt; - Stored results of computed functions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deps&lt;/code&gt; - Some way of storing the relationships&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I went with &lt;code&gt;deps&lt;/code&gt; over &lt;code&gt;dependencies&lt;/code&gt; mostly for width considerations in our code. Let's create that starting object.&lt;/p&gt;

&lt;p&gt;There are lots of ways we could arrange this data structure, but lets start here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;static&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a Proxy
&lt;/h2&gt;

&lt;p&gt;Proxies accept a &lt;code&gt;target&lt;/code&gt; object (or function!) and a &lt;code&gt;handler&lt;/code&gt;; an object which defines how the Proxy works. Handlers contain zero or more &lt;em&gt;traps&lt;/em&gt; - a term that dates back at least to 1955 which was used to interrupt the usual execution of a program, typically for debugging. These traps will prevent the normal execution of property access to do all the interesting things we want from our Proxy. There are a large number of different traps available to us – &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#object_internal_methods" rel="noopener noreferrer"&gt;the object internal methods&lt;/a&gt; - but we're going to focus on &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Temporarily do the default&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Temporarily do the default&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some notes on these function arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;target&lt;/code&gt; - The target object specified when we created the proxy. This avoids any need for a closure or external reference to access the original object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;property&lt;/code&gt; - The name of the property being accessed on the proxy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; - For &lt;code&gt;set&lt;/code&gt; we need to pass the actual value to be set.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;receiver&lt;/code&gt; - In some cases we will need to refer back to the object that received the property access request. This – our proxy – is the receiver.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we have a proxy object &lt;code&gt;data&lt;/code&gt; that will access the target &lt;code&gt;source&lt;/code&gt;. It has traps for &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt;, but they don't do anything special yet. We've temporarily inserted &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect" rel="noopener noreferrer"&gt;&lt;code&gt;Reflect&lt;/code&gt;&lt;/a&gt; calls to provide the original ability of an object getter and setter. At this point the proxy is just a pass-through for normal operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set a property on the proxy&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// The same thing happens on the source&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Segmented Set
&lt;/h2&gt;

&lt;p&gt;We specified we want to separate &lt;code&gt;computed&lt;/code&gt; functions from &lt;code&gt;static&lt;/code&gt;. We could let the user set these directly, but I want to abstract the knowledge of the source object where possible. So let's update the &lt;code&gt;set&lt;/code&gt; trap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Temporarily do the default&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it will save values and functions to different places.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set a property on the proxy&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// This goes into values on the source&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 2&lt;/span&gt;

&lt;span class="c1"&gt;// But we haven't updated our getter&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, our getter needs work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Return Values
&lt;/h2&gt;

&lt;p&gt;We have two different places data can live. At first I was tempted to use nullish coalescing to check for &lt;code&gt;static&lt;/code&gt; or &lt;code&gt;computed&lt;/code&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this would cause incorrect behavior for &lt;em&gt;intentional&lt;/em&gt; nullish values. We need to use &lt;code&gt;in&lt;/code&gt; or, better yet, &lt;code&gt;Object.hasOwn&lt;/code&gt; to get values that exist on the correct object, even if they are &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;undefined&lt;/code&gt;, or falsy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Return Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Default is undefined, just like normal.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Return Validation
&lt;/h3&gt;

&lt;p&gt;You'll notice we &lt;em&gt;executed&lt;/em&gt; the &lt;code&gt;computed&lt;/code&gt; entry rather than returning the function. This lets us avoid knowing if a value is computed or not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 2&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Computed Data Access
&lt;/h2&gt;

&lt;p&gt;Computed values are not very useful if they can't access the other data to perform work, though, so we need to update our &lt;code&gt;get&lt;/code&gt; trap to support that change. There are a couple ways we could do that.&lt;/p&gt;

&lt;p&gt;We can pass the data proxy to the computed functions as the first argument. This gives them full access to static and computed values. This is where the &lt;code&gt;receiver&lt;/code&gt; argument becomes useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Computed Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Log the computation for demonstration&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Computing &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Default is undefined, just like normal.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Computed Validation
&lt;/h3&gt;

&lt;p&gt;Now, we can use static and computed properties as values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Some static values;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Some computed values.&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaCircle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaTriangle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// 3&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaCircle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 28.274333882308138&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Computing areaCircle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// 50&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Computing areaRect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaTriangle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 25&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Computing areaTriangle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Computing areaRect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;code&gt;areaTriangle&lt;/code&gt; uses &lt;code&gt;areaRect&lt;/code&gt;. Because we passed the proxy and not the "source" object, we can consume computed values inside computed values. But this means we're computing &lt;code&gt;areaRect&lt;/code&gt; twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache Functions
&lt;/h2&gt;

&lt;p&gt;While our examples are pretty trivial, computed values might end be expensive operations, so caching the output will prevent us from doing the same work repeatedly. Adding caching is very straightforward. We can check for a cached value first, and if one is not found perform the work and add it to the cache before returning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Log the computation for demonstration&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Computing &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Default is undefined, just like normal.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cache Validation
&lt;/h3&gt;

&lt;p&gt;Now we'll only process &lt;code&gt;areaRect&lt;/code&gt; once. The second time it is accessed, we use the cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// 3&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaCircle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 28.274333882308138&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Computing areaCircle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// 50&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Computing areaRect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaTriangle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 25&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Computing areaTriangle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Track Dependencies
&lt;/h2&gt;

&lt;p&gt;Now we have computed properties and caching, but if we change something our cache can get out of date. A naive cache strategy would be to wipe the whole cache whenever any value is set, but that's not very helpful. What we need is to track our dependencies. And proxies are well-suited to solve this problem.&lt;/p&gt;

&lt;p&gt;We've already seen that we can intercept property access with a &lt;code&gt;get&lt;/code&gt; trap. So we can see each property that is accessed. We can use a second proxy specific to computed properties to solve this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency Proxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depsTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depTarget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;depTarget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This get-only proxy will record each property name accessed using a &lt;code&gt;Set&lt;/code&gt; to avoid duplicates. This adds some overhead to using a computed value, but that's why we have caching!&lt;/p&gt;

&lt;p&gt;Once we have the tracked dependencies, we can add them to the &lt;code&gt;deps&lt;/code&gt; object. Because we're concerned about the dependencies changing, we want the keys inside &lt;code&gt;deps&lt;/code&gt; to be the properties we depend on. We can take the &lt;code&gt;tracker&lt;/code&gt; and populate deps from it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Something very roughly like this&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;areaCircle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependency Code
&lt;/h3&gt;

&lt;p&gt;Let's put that all together. I've also eliminated the &lt;code&gt;console.log&lt;/code&gt; in the &lt;code&gt;computed&lt;/code&gt; logic. Adding log statements can help you understand the order of operations, but you can add your own if you are interested.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depsTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depTarget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;depTarget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;depsTracker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Default is undefined, just like normal.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validated Dependencies
&lt;/h3&gt;

&lt;p&gt;Now, let's run the same code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Some static values;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Some computed values.&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaCircle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaTriangle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaCircle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 28.274333882308138&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// 50&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaTriangle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And check our deps object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;areaCircle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;areaRect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;areaRect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;areaRect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;areaTriangle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that we tracked each direct dependency. We aren't using that information, yet, just collecting it, so we'll need to look at our &lt;code&gt;set&lt;/code&gt; trap for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clear Cache
&lt;/h2&gt;

&lt;p&gt;When we call &lt;code&gt;set&lt;/code&gt; we know the &lt;code&gt;property&lt;/code&gt; that is changing, so it's a perfect time to check the &lt;code&gt;deps&lt;/code&gt; to see if we need to remove anything from the cache.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Make a function for recursion&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depClean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nf"&gt;depClean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;depClean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created a small recursive function to ensure any nested dependencies – like &lt;code&gt;areaTriangle&lt;/code&gt; – are cleared. We didn't bother with any "exists" checks beforehand. A property should have no dependencies to check if it is being set for the first time. &lt;/p&gt;

&lt;h3&gt;
  
  
  Invalidation Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depsTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depTarget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;depTarget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;depsTracker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Default is undefined, just like normal.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Make a function for recursion&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depClean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nf"&gt;depClean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;depClean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Invalidation…Validation
&lt;/h3&gt;

&lt;p&gt;Let's perform some operations to populate the cache, and then prove that the cache is cleared base on dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Access properties to create cache entries&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// 3&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaCircle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 28.274333882308138&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// 50&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaTriangle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 25&lt;/span&gt;

&lt;span class="c1"&gt;// View the cache&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;areaCircle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;28.274333882308138&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;areaRect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;areaTriangle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Change a property with dependencies&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// View the updated cache&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;areaCircle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;28.274333882308138&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that changing &lt;code&gt;base&lt;/code&gt; cleared both &lt;code&gt;areaRect&lt;/code&gt; and &lt;code&gt;areaTriangle&lt;/code&gt;, so our recursive dependency check worked.&lt;/p&gt;

&lt;p&gt;Now, if we call those computed properties again, their values should change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// 40&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;areaTriangle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;We've now worked through each of our requirements, and we have a basic reactive data model. We can support static and computed properties. We can access computed properties without knowing there are functions underneath. We cache those computed values, and create a dependency tree that lets us invalidate that cache when properties are updated.&lt;/p&gt;

&lt;p&gt;Let's take a look at the completed code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;static&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check for static properties first&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Check the cache for saved computed properties&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasOwn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Build the dependencies and cache the compute value&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depsTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depTarget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;depTarget&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;depProp&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;depsTracker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Default is undefined, just like normal.&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Use recursion to clean nested dependencies&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depClean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nf"&gt;depClean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;depClean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;property&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Proxy Proof-of-Concept
&lt;/h3&gt;

&lt;p&gt;All this in under 60 lines of code! While it doesn't do everything, proxies provided us the tools to build a system to support automatic dependency detection and caching quickly and relatively easily. &lt;/p&gt;

&lt;h2&gt;
  
  
  Your Turn?
&lt;/h2&gt;

&lt;p&gt;There are plenty of things to add and improve in this design. Try your hand at adding onto the code and see where it takes you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for &lt;code&gt;in&lt;/code&gt; and &lt;code&gt;Object.keys&lt;/code&gt; from the proxy.&lt;/li&gt;
&lt;li&gt;Add support for deleting properties.&lt;/li&gt;
&lt;li&gt;Clean up dependencies when a property is deleted.&lt;/li&gt;
&lt;li&gt;Only calculate the dependencies when the computed property changes. &lt;/li&gt;
&lt;li&gt;Support changing properties from static to computed or &lt;em&gt;vice versa&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Avoid dependency checks on initial &lt;code&gt;set&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Should we re-compute values when dependencies change, or wait for them to be accessed?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Proxies provide nearly unlimited flexibility in JavaScript. Starting from a rough idea we built up a reactive data layer with caching and dependency tracking.&lt;/p&gt;

&lt;p&gt;I hope you found this article interesting. Are there better ways to create this functionality? Is part of this confusing and needs better explanation? Let me know!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Promise Hashes</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Sun, 28 Sep 2025 00:17:52 +0000</pubDate>
      <link>https://forem.com/oculus42/promise-hashes-394j</link>
      <guid>https://forem.com/oculus42/promise-hashes-394j</guid>
      <description>&lt;p&gt;Promises provide powerful asynchronous program flow-control, but one of the things they do not currently handle are maps/hashes of promises. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" rel="noopener noreferrer"&gt;&lt;code&gt;Promise.all()&lt;/code&gt;&lt;/a&gt; accepts an array, but it can be cumbersome to track things by index when multiple async actions are needed, e.g. setting up a form or executing an action that requires permissions.&lt;/p&gt;

&lt;p&gt;It would be nice, sometimes, to be able to pass a named object instead of an array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// An imaginary future where this concept exists&lt;/span&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getAppConfig&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;i18n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getFormattingDirectives&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;l10n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getLocaleStrings&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getFormRules&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While we don't have such a method, creating one turns out to be really straightforward. Let's take a look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Accept a "promise hash" – an object with key-value pairs where the values &lt;em&gt;may&lt;/em&gt; be promises.&lt;/li&gt;
&lt;li&gt;Extract the values as an array and pass them to &lt;code&gt;Promise.all()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If successful, re-connect the array of results with the original keys and return this new "value hash". Otherwise, pass the rejection.&lt;/li&gt;
&lt;li&gt;Return the promise chain containing the "value has" object.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Not All Promises
&lt;/h3&gt;

&lt;p&gt;Note the word &lt;em&gt;may&lt;/em&gt; in the first requirement. If you aren't familiar with the inner workings of &lt;code&gt;Promise.all()&lt;/code&gt; you might not know that it accepts non-promises and treats them the same as resolved promises. This provides flexibility so a function can be asynchronous or not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="c1"&gt;// Immediately resolved promise&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="c1"&gt;// Normal values or function returns are treated as "resolved"&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// A promise with a little delay&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="c1"&gt;// This function is returned as a value, not executed&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// [1, 2, 3, ƒ() ])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't something you have to worry about, but it can really come in handy when testing &amp;amp; mocking, as you don't have to create async functions if they don't initiate promise chains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding
&lt;/h2&gt;

&lt;p&gt;Now, let's build based on our requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accept a "promise hash"
&lt;/h3&gt;

&lt;p&gt;Not much to accomplish in this step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extract the values...to Promise.all()
&lt;/h3&gt;

&lt;p&gt;Updated versions of JavaScript make this quite simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  If successful...return the new "value hash"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice we didn't do anything about rejection handling. We get that "for free" with &lt;code&gt;Promise.all()&lt;/code&gt;. The first promise to reject gets the rejection, so that is passed through automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Return the promise chain
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt; 
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's not a lot to it. The object prototype methods really simplify the transformation from and to object for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardening
&lt;/h2&gt;

&lt;p&gt;I'm going to pause here for a change. While this meets our requirements, there's a &lt;em&gt;time difference&lt;/em&gt; between when we extract the &lt;code&gt;keys&lt;/code&gt; and &lt;code&gt;values&lt;/code&gt;. This is unlikely to cause a problem, but it &lt;em&gt;can&lt;/em&gt; if we modify the inputHash object before the promises resolve.&lt;/p&gt;

&lt;p&gt;To prevent this, even from intentionally mischievous code, we can use &lt;code&gt;Object.entries()&lt;/code&gt; to extract the matching pairs only once to ensure they always line up. We could do this more efficiently but I'd be more concerned about the consuming code if we get to a point where the object hash transform is a performance issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Convert the object once for stable ordering.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Split keys and values&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Merge the results and keys back together&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;])));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could write the last bit differently, depending on your comfort reading nested function operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Merge the results and keys back together&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;valueHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;valueHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;A simple demonstration of the design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;promiseHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// { a: 1, b: 2 }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;p&gt;Every now and then I see a group of similar requests in a &lt;code&gt;Promise.all()&lt;/code&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entitlements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nf"&gt;getUserPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nf"&gt;getPostPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nf"&gt;getModeratorPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt; &lt;span class="nx"&gt;userPerms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;postPerms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modPerms&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userPerms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canEdit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is &lt;em&gt;brittle&lt;/em&gt;. It depends on the developer keeping two "unrelated" arrays in sync. If we add, remove, or rearrange the entries, we can unintentionally change or break the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entitlements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="c1"&gt;// Adding an entry out-of-order breaks the .then()&lt;/span&gt;
  &lt;span class="nf"&gt;getAdminPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nf"&gt;getUserPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nf"&gt;getPostPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nf"&gt;getModeratorPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt; &lt;span class="nx"&gt;userPerms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;postPerms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;modPerms&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userPerms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canEdit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hashes prevent this problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entitlements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;promiseHash&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// Adding a key doesn't change the .then()&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getAdminPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getUserPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getPostPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getModeratorPermissions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;perms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;perms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canEdit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Variations on a Theme
&lt;/h2&gt;

&lt;p&gt;We could also use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled" rel="noopener noreferrer"&gt;&lt;code&gt;Promise.allSettled()&lt;/code&gt;&lt;/a&gt; underneath this design instead of &lt;code&gt;Promise.all()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseSettledHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Convert the object once for stable ordering.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Split keys and values&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Merge the results and keys back together&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allSettled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;])));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could even make this an optional second argument so you can choose which operation handles your hash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promiseHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useSettled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Convert the object once for stable ordering.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputHash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Split keys and values&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Merge the results and keys back together&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;useSettled&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allSettled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;])));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll note that we included the full name in each call. Promise methods must be called on the Promise object, so the alternate code below throws an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;useSettled&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allSettled&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// TypeError: Promise.all called on non-object&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Honestly, there isn't that much to creating a Promise hash, but it can be a useful tool if you find yourself collecting a few promises. There may be other ways to design your application design to avoid the &lt;code&gt;Promise.all()&lt;/code&gt; risks, but a simple wrapper to provide hash support can make your code a little easier to follow.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Git Bits: The Graph</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Sat, 20 Sep 2025 17:47:27 +0000</pubDate>
      <link>https://forem.com/oculus42/git-bits-the-graph-2cf9</link>
      <guid>https://forem.com/oculus42/git-bits-the-graph-2cf9</guid>
      <description>&lt;p&gt;Git is...complicated. This series focuses on breaking git down into commit-sized pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Graph
&lt;/h2&gt;

&lt;p&gt;The foundation of git is a &lt;a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph" rel="noopener noreferrer"&gt;directed acyclic graph&lt;/a&gt;. You can dive into the Wikipedia article for more information than you probably want, but the basics as they relate to Git are this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each commit in a Git repo is a node on the graph.&lt;/li&gt;
&lt;li&gt;Each commit "knows about" (references) the one or more nodes that came before it.&lt;/li&gt;
&lt;li&gt;We can trace the history of commits through the graph.&lt;/li&gt;
&lt;li&gt;We can't &lt;em&gt;go backward&lt;/em&gt;, so there are no &lt;em&gt;loops&lt;/em&gt;, but it is possible to have multiple paths between two nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Notes about Graphs
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tt1eebhktkvij8pxkpx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tt1eebhktkvij8pxkpx.png" alt="Example graph of commits on multiple branches" width="800" height="366"&gt;&lt;/a&gt;&lt;br&gt;Fig.1 - Example graph of commits on multiple branches 
  &lt;/p&gt;

&lt;p&gt;This graph was created with the &lt;a href="https://mermaid.js.org/syntax/gitgraph.html" rel="noopener noreferrer"&gt;absolutely amazing Mermaid.js&lt;/a&gt;. It represents commits with a number and a hash by default, like &lt;code&gt;0-2b99de5&lt;/code&gt;. The number is the order they appear in the graph definition. The hash is a randomly generated hexadecimal to simulate what actual hashes in git look like. &lt;/p&gt;

&lt;h3&gt;
  
  
  Nodes
&lt;/h3&gt;

&lt;p&gt;There are mostly two types of "nodes" on the graph in git: commits and merges.&lt;/p&gt;

&lt;p&gt;You can see that &lt;code&gt;2-a0f289f&lt;/code&gt; on the &lt;code&gt;develop&lt;/code&gt; branch has a line that leads back to &lt;code&gt;1-10ed042&lt;/code&gt;, which in turn has a line that leads back to &lt;code&gt;0-2b99de5&lt;/code&gt;, the start of this graph. You can walk the graph and see the history of each commit in this way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merges
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F33v1zgiw9uau0ot310wy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F33v1zgiw9uau0ot310wy.png" alt=" " width="800" height="366"&gt;&lt;/a&gt;&lt;br&gt;Fig.2 - Example graph pointing out a merge commit 
  &lt;/p&gt;

&lt;p&gt;Looking at the graph, you can see a node where the changes from the &lt;code&gt;feature&lt;/code&gt; branch are merged into &lt;code&gt;develop&lt;/code&gt;. That node is connected to two &lt;em&gt;parents&lt;/em&gt;: &lt;code&gt;5-ca2ed61&lt;/code&gt; on &lt;code&gt;develop&lt;/code&gt; and &lt;code&gt;6-fdcd8eb&lt;/code&gt; on &lt;code&gt;feature&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is possible to perform a merge of more than two sources – an "octopus merge" – but it is very uncommon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Branches
&lt;/h2&gt;

&lt;p&gt;Git's branching system is sometimes referred to as &lt;em&gt;lightweight branching&lt;/em&gt;. Compared to some version control systems which make whole copies for new branches, git branches are essentially just a pointer to a commit on the graph, so making a new branch is basically instantaneous. Let's see that in action.&lt;/p&gt;

&lt;p&gt;I've created an empty repo and committed a simple README file. If I run &lt;code&gt;git log&lt;/code&gt;, it gives me the commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 1df473a77206a3208ef8c9213dbd298e0ad9b369 (HEAD -&amp;gt; main)
Author: Me &amp;lt;me@example.com&amp;gt;
Date:   Fri Sep 19 12:10:39 2025 -0400

    Initial commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I make a new branch with &lt;code&gt;git branch develop&lt;/code&gt; and run &lt;code&gt;git log&lt;/code&gt; again, I see that &lt;strong&gt;this same commit&lt;/strong&gt; is now represented on &lt;code&gt;main&lt;/code&gt; and &lt;code&gt;develop&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 1df473a77206a3208ef8c9213dbd298e0ad9b369 (HEAD -&amp;gt; main, develop)
Author: Me &amp;lt;me@example.com&amp;gt;
Date:   Fri Sep 19 12:10:39 2025 -0400

    Initial commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though I've created a branch, I haven't changed the contents of the repo. The branch is &lt;em&gt;metadata&lt;/em&gt; – information about the commits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Divergence
&lt;/h3&gt;

&lt;p&gt;Interestingly, the branch doesn't keep track of every commit on it. The branch only has to know about &lt;em&gt;the latest commit&lt;/em&gt;, because that one knows about the commit that came before it, and so on.&lt;/p&gt;

&lt;p&gt;Going back to our branch diagram from earlier, the &lt;code&gt;develop&lt;/code&gt; branch can build the list of commits in its history, and discover where it has one in common with &lt;code&gt;main&lt;/code&gt; – &lt;code&gt;0-2b99de5&lt;/code&gt;, so we can say &lt;code&gt;develop&lt;/code&gt; &lt;strong&gt;diverged&lt;/strong&gt; from &lt;code&gt;main&lt;/code&gt; at &lt;code&gt;1-10ed042&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Timeless Representation
&lt;/h2&gt;

&lt;p&gt;One important distinction is that &lt;em&gt;time&lt;/em&gt; is not actually a necessary part of the graph. We think of them in time order because it's helpful to us, but the graph is really only concerned with &lt;em&gt;direction&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The HEAD
&lt;/h3&gt;

&lt;p&gt;While there are timestamps on commits, there's really only one time git is interested in: right now. Git represents "wherever you are right now" as a value called &lt;code&gt;HEAD&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If we look at that &lt;code&gt;git log&lt;/code&gt; command again, we can see that &lt;code&gt;HEAD&lt;/code&gt; is pointing to the &lt;code&gt;main&lt;/code&gt; branch, even though we also created a &lt;code&gt;develop&lt;/code&gt; branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 1df473a77206a3208ef8c9213dbd298e0ad9b369 (HEAD -&amp;gt; main, develop)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we &lt;code&gt;git checkout develop&lt;/code&gt; we'll still be on the same commit, but &lt;code&gt;HEAD&lt;/code&gt; will be different.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 1df473a77206a3208ef8c9213dbd298e0ad9b369 (HEAD -&amp;gt; develop, main)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use commands to move back from &lt;code&gt;HEAD&lt;/code&gt;, like &lt;code&gt;git checkout HEAD~1&lt;/code&gt; which will check out one commit before the current &lt;code&gt;HEAD&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-writing History
&lt;/h2&gt;

&lt;p&gt;The Graph keeps track of the relationship between things, but it is not written in stone. There are several ways you can change the past, but, just as in the movies, there are risks of doing so.&lt;/p&gt;

&lt;p&gt;Most of the concerns with re-writing history are around &lt;em&gt;shared branches&lt;/em&gt;. Because git is &lt;em&gt;decentralized&lt;/em&gt;, there isn't something that says &lt;em&gt;which history&lt;/em&gt; is correct if histories diverge. You create a different timeline that didn't happen for everyone. It's a science-fiction nightmare, but for code.&lt;/p&gt;

&lt;p&gt;Still, when you are on your personal branch, re-writing history can be really useful.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git commit --amend&lt;/code&gt; let's you change the last commit you created. If you misspelled a word or forgot an update, &lt;code&gt;--amend&lt;/code&gt; can let you create a history where that didn't happen. For simple changes that exist only for you, this is an easy way to avoid having a commit with an obvious error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebase
&lt;/h3&gt;

&lt;p&gt;Rebasing has a bad reputation. Rebasing is when you change history by "moving the start of your branch". This usually happens during parallel development when two or more people are submitting changes to a branch or repository. When one developer's work is accepted and merged, another developer may need to incorporate that work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ljydr9zld255kam9r90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ljydr9zld255kam9r90.png" alt="Mermaid GitGraph showing parallel development" width="800" height="352"&gt;&lt;/a&gt;&lt;br&gt;Fig.3 - Branch feature1 is merged in while we are working on feature2 
  &lt;/p&gt;

&lt;p&gt;Now that &lt;code&gt;feature1&lt;/code&gt; is merged, we have a couple options. We could create a merge from &lt;code&gt;main&lt;/code&gt; to &lt;code&gt;feature2&lt;/code&gt; with &lt;code&gt;git merge main&lt;/code&gt; while we are on the &lt;code&gt;feature2&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94rks4dq437o0pprdrbb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94rks4dq437o0pprdrbb.png" alt="Mermaid GitGraph showing a merge from main to a feature branch" width="800" height="352"&gt;&lt;/a&gt;&lt;br&gt;Fig.4 - Merge main into feature2 
  &lt;/p&gt;

&lt;p&gt;If &lt;code&gt;feature2&lt;/code&gt; is a branch that multiple people are working on, that's usually the correct solution. But if it's just one developer, using &lt;code&gt;git rebase main&lt;/code&gt; creates a "cleaner" history, because there is no longer a point where history diverged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91tcsta2qnsv4d14afmx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91tcsta2qnsv4d14afmx.png" alt="Mermaid GitGraph showing feature2 branch after rebasing" width="800" height="352"&gt;&lt;/a&gt;&lt;br&gt;Fig.5 - Rebase feature2 onto main
  &lt;/p&gt;

&lt;p&gt;We can say, "&lt;code&gt;feature2&lt;/code&gt; was rebased onto &lt;code&gt;main&lt;/code&gt;." History has changed, and there is no record that it was ever attached to the earlier commit.&lt;/p&gt;

&lt;p&gt;We'll talk more about rebasing another time. But from the simple diagrams we can see that rebase picks up part of the graph and connects it somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Some information in this article comes from the unassailable &lt;a href="https://git-scm.com" rel="noopener noreferrer"&gt;git-scm.com&lt;/a&gt; site and the &lt;a href="https://git-scm.com/book/en/v2" rel="noopener noreferrer"&gt;Pro Git Book&lt;/a&gt; available to read there for free.&lt;/p&gt;

&lt;p&gt;The section &lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell" rel="noopener noreferrer"&gt;Branches in a Nutshell&lt;/a&gt; provides some really good detail and diagrams on the graph and branching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Let's bring it back together. Git is a directed graph of commits, each representing a snapshot of the repo. Branches and tags are metadata that point to specific commits. You can add to, change, or even move parts of the graph.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s1"&gt;'The Graph'&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Git Bits: Basic Terminology</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Fri, 19 Sep 2025 10:00:00 +0000</pubDate>
      <link>https://forem.com/oculus42/git-bits-basic-terminology-do7</link>
      <guid>https://forem.com/oculus42/git-bits-basic-terminology-do7</guid>
      <description>&lt;p&gt;Git is...complicated. This series focuses on breaking git down into commit-sized pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working Words
&lt;/h2&gt;

&lt;p&gt;The decentralized nature of Git creates some confusion about the different words you might use, so let's try to clear that up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repo
&lt;/h3&gt;

&lt;p&gt;Repository, or repo, is the place data is stored. People sometimes will use "project" interchangeably with a repo. The repo usually contains the project history.&lt;/p&gt;

&lt;p&gt;When you work with a project on GitHub, there is a repo on the server. When you &lt;strong&gt;clone&lt;/strong&gt; that repo, there is a repo on your computer as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/oculus42/git-bits-working-without-a-server-2ncp"&gt;A server isn't necessary, though&lt;/a&gt;. If you run &lt;code&gt;git init&lt;/code&gt; you &lt;em&gt;create a new repo&lt;/em&gt; on your computer. It doesn't exist anywhere else in that moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone
&lt;/h3&gt;

&lt;p&gt;We usually refer to &lt;em&gt;our copy&lt;/em&gt; of a repo as a &lt;strong&gt;clone&lt;/strong&gt;. This helps differentiate between our copy and the server. While there is no actual distinction, we tend to still think in that centralized concept, so most people refer to the &lt;em&gt;server's copy&lt;/em&gt; as the repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fork
&lt;/h3&gt;

&lt;p&gt;Forking is the idea that you &lt;em&gt;split&lt;/em&gt; from the original repo and have your own copy. Perhaps you want to do something different with the project, or this can be the first step in making a copy so you can contribute in the open-source fork-and-pull model.&lt;/p&gt;

&lt;p&gt;This terminology is really about the centralized server-side repositories. Clones are for working with the original server-side repository. Forks are making a copy of that server-side repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remote
&lt;/h3&gt;

&lt;p&gt;A remote is another copy of the repo, usually on a server. When you clone a repo, it will create a default remote of &lt;strong&gt;origin&lt;/strong&gt; pointing back to the source. In many branch-based workflows, you only set up one remote. In a fork-and-pull style, you might set up &lt;strong&gt;origin&lt;/strong&gt; for your personal fork and &lt;strong&gt;upstream&lt;/strong&gt; for the repository you forked from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Does this line up with how you use these words? Let me know!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s1"&gt;'Basic Terminology'&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>programming</category>
      <category>beginners</category>
      <category>git</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Git Bits: Working Without a Server</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Thu, 18 Sep 2025 17:14:39 +0000</pubDate>
      <link>https://forem.com/oculus42/git-bits-working-without-a-server-2ncp</link>
      <guid>https://forem.com/oculus42/git-bits-working-without-a-server-2ncp</guid>
      <description>&lt;p&gt;Git is...complicated. This series focuses on breaking git down into commit-sized pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Server Is Optional
&lt;/h2&gt;

&lt;p&gt;Git is a distributed version control system. Most of the time this isn't very relevant, because we usually work with a single, central repository, but &lt;em&gt;we don't have to&lt;/em&gt;. Because we clone the whole repo to our computers, each copy contains everything.&lt;/p&gt;

&lt;p&gt;Unlike centralized version control, you don't have to stop collaborating when the server goes down. You can still sync and share  and merge work directly with other developers, whether there's an outage or you are working in an environment without Internet access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decentralized Differences
&lt;/h2&gt;

&lt;p&gt;This disconnect from "traditional" centralized &lt;a href="https://en.wikipedia.org/wiki/Version_control" rel="noopener noreferrer"&gt;version control systems dating back to the 1960s&lt;/a&gt; can be hard to grasp, as git isn't like most of our models of servers we connect to. We download and view content from a web server, but the server remains the "source of truth". If we make a post or comment on this site, they are stored on the server, not our computer.&lt;/p&gt;

&lt;p&gt;You might think of git repos more like operating systems. The device where you are reading this has a full copy of its operating system. It might pull updates from a server, but we each have a whole "working copy" of the OS.&lt;/p&gt;

&lt;p&gt;It's more than that, because programs and operating systems are generally &lt;em&gt;artifacts&lt;/em&gt; (outputs) of development, and git typically stores the source (inputs).&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Serverless
&lt;/h2&gt;

&lt;p&gt;Git supports connections using &lt;code&gt;https://&lt;/code&gt; and &lt;code&gt;ssh://&lt;/code&gt; – and its own &lt;code&gt;git://&lt;/code&gt; protocol – but also a &lt;a href="https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_local_protocol" rel="noopener noreferrer"&gt;local file protocol&lt;/a&gt; which allows you to push and pull from the file system. If your repository server is down, you can still share work with others.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set up a file system remote&lt;/span&gt;
git remote add teammate /Users/alex/work/project

&lt;span class="c"&gt;# Load and merge your team's updates&lt;/span&gt;
git fetch teammate
git merge teammate/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git even supports offline transfer of updates using &lt;a href="https://git-scm.com/docs/git-bundle" rel="noopener noreferrer"&gt;git-bundle&lt;/a&gt;, so you can bundle up changes from a common point that you know another person has in their local clone of the repo, and send them the changes. This was the original use of git. Make changes, bundle them into a patch file, and send them off to a maintainer of the project.&lt;/p&gt;

&lt;p&gt;All you need is two laptops and a flash drive to use this flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Bundle the differences between main and my feature&lt;/span&gt;
git bundle create updates.bundle main..feature

&lt;span class="c"&gt;# Merge in a bundle&lt;/span&gt;
git pull path/to/updates.bundle main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Each Git clone is a complete repository with its own history. Two developers can collaborate using the local file protocol or by exchanging offline bundles. The server is &lt;em&gt;convenient&lt;/em&gt; but not required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s1"&gt;'Working Without a Server'&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>beginners</category>
      <category>git</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Mac Headaches: External Monitors</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Wed, 10 Sep 2025 01:57:01 +0000</pubDate>
      <link>https://forem.com/oculus42/mac-headaches-external-monitors-1ni7</link>
      <guid>https://forem.com/oculus42/mac-headaches-external-monitors-1ni7</guid>
      <description>&lt;p&gt;Macs have been the "It Just Works" computer for decades, but connecting multiple monitors frequently creates questions or problems. I connected &lt;em&gt;five monitors&lt;/em&gt; to a Macintosh IIx back in the 1990s, so what happened?! Back then it was very straightforward: each monitor required a separate video card and cable from the computer. A lot has changed and &lt;em&gt;it can be complicated&lt;/em&gt; to connect multiple displays, now. Let's get into it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your Mac May Vary&lt;/li&gt;
&lt;li&gt;The Simple Solution&lt;/li&gt;
&lt;li&gt;USB-C Isn't One Thing&lt;/li&gt;
&lt;li&gt;DisplayPort: USB v Thunderbolt&lt;/li&gt;
&lt;li&gt;Bandwidth Bummers&lt;/li&gt;
&lt;li&gt;Multi-Stream Transport&lt;/li&gt;
&lt;li&gt;DisplayLink Docks&lt;/li&gt;
&lt;li&gt;Docks vs. Hubs&lt;/li&gt;
&lt;li&gt;Other Articles&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Your Mac May Vary
&lt;/h2&gt;

&lt;p&gt;Different computers have different external monitor support, but this is true for Macs even more than most. As good as the Apple Silicon processor line is, it brings more variations. Even in the same model and year of computer (e.g. 2025 MacBook Pro), differences exist.&lt;/p&gt;

&lt;p&gt;There are a number of Apple Support articles that can help you identify your computer, which then link to the Tech Specs for the different models. If you are considering purchasing a new or used Mac and have specific display requirements, these pages may help.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identify Your Mac
&lt;/h3&gt;

&lt;p&gt;You can start with the &lt;a href="https://support.apple.com/en-us/102604" rel="noopener noreferrer"&gt;Identify the model of your Apple device&lt;/a&gt; article, or go to the specific type directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/108052" rel="noopener noreferrer"&gt;Identify your MacBook Pro model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/102869" rel="noopener noreferrer"&gt;Identify your MacBook Air model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/102852" rel="noopener noreferrer"&gt;Identify your Mac mini model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/108054" rel="noopener noreferrer"&gt;Identify your iMac model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/102231" rel="noopener noreferrer"&gt;Identify your Mac Studio model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External Monitor Support
&lt;/h3&gt;

&lt;p&gt;Apple has support articles for understanding how many external displays your computer supports. These are a good starting point, but there is a lot of additional nuance between these "base" specs and a specific desk setup, so keep that in min.&lt;/p&gt;

&lt;p&gt;Monitor support varies mostly by processor type. The MacBook Pro can have a "base" M-series processor, or a Pro or Max chip. Each of these have different rules by processor type and &lt;em&gt;generation&lt;/em&gt;, so an M2 and an M3 may not be the same.&lt;/p&gt;

&lt;p&gt;For instance, the Apple Silicon MacBook Air supported only one external monitor until the MacBook Air M3 (March, 2024).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://support.apple.com/en-us/101571" rel="noopener noreferrer"&gt;How many displays can be connected to MacBook Pro&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://support.apple.com/en-us/122212" rel="noopener noreferrer"&gt;How many displays can be connected to MacBook Air&lt;/a&gt; - M4-specific&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.apple.com/en-us/117373" rel="noopener noreferrer"&gt;Use dual monitors with your MacBook Air and MacBook Pro with M3 chip&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll probably notice most of these articles talk specifically about Thunderbolt rather than USB. We'll get to that distinction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closed Lid Limits
&lt;/h3&gt;

&lt;p&gt;How you use the computer makes a difference, too. I bought a 2024 M3 MacBook Air, and it's great, but I wanted to use two external monitors. It supports that, but not how I expected. Some Macs, like the one I own, only support multiple external monitors &lt;em&gt;with the lid closed&lt;/em&gt;. This is also called "clamshell mode".&lt;/p&gt;

&lt;p&gt;As long as it has to run the &lt;em&gt;internal&lt;/em&gt; display, my M3 MacBook Air will power only one external display. I have a setup where I use two displays with the laptop to the side for "auxiliary" content like email or Discord, but I couldn't do that with the M3 Air. It's a great computer, but that limitation led me to get a MacBook Pro M4 Max.&lt;/p&gt;

&lt;p&gt;If you use your laptop with the lid closed – in a dock, tucked below the monitor, or just off to the side – you may be able to avoid the "Pro tax" while still getting two external displays.&lt;/p&gt;

&lt;p&gt;It hopefully goes without saying, but in a closed-lid setup you need an external keyboard and pointing device.&lt;/p&gt;

&lt;h3&gt;
  
  
  Special Cases
&lt;/h3&gt;

&lt;p&gt;We won't dig into them, but your Mac also supports some special cases, like &lt;a href="https://support.apple.com/guide/mac-help/use-your-ipad-as-a-second-display-mchlf3c6f7ae/15.0/mac/15.0" rel="noopener noreferrer"&gt;using an iPad as a second screen with Sidecar&lt;/a&gt; or streaming to a television  using &lt;a href="https://www.apple.com/airplay/" rel="noopener noreferrer"&gt;AirPlay&lt;/a&gt;, where you can mirror or extend your desktop, again depending on the device. It's not currently clear to me how these work with the limit on monitors for specific devices, but I wanted to mention these possibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Simple Solution
&lt;/h2&gt;

&lt;p&gt;Before we go too much further; most Macs support multiple external displays. You can usually accomplish that the same was I did back in the 90s – every monitor connected directly to the computer. This is generally fine on desktops but not ideal with laptops that sometimes leave the desk.&lt;/p&gt;

&lt;p&gt;Still, if your Mac has the ports and hardware support, this is the most reliable solution. If you – like most people – are looking for a  one-cable (or at least &lt;em&gt;few cable&lt;/em&gt;) solution, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  USB-C Isn't One Thing
&lt;/h2&gt;

&lt;p&gt;The biggest confusion comes from the USB-C port. The &lt;strong&gt;USB&lt;/strong&gt; name covers both physical connectors, like USB-C, as well as data and power specifications.&lt;/p&gt;

&lt;p&gt;If you're interested, &lt;a href="https://www.conwire.com/blog/ultimate-guide-usb-cables/" rel="noopener noreferrer"&gt;Consolidated Cables has an article about USB ports and basic specs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.conwire.com/blog/ultimate-guide-usb-cables/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wswifobjvlh8fq3lfc2.png" alt="USB Ports Comparison from Consolidated Cables" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But wait, there's more! USB-C connectors also support "alternate modes" which let the same connector provide DisplayPort, Thunderbolt 3 or later, and other less-common signals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/USB-C" rel="noopener noreferrer"&gt;Wikipedia's USB-C article&lt;/a&gt; is long but thorough if you want details. I recognize most people truly don't care, but it is good to know it's an option.&lt;/p&gt;

&lt;p&gt;The three relevant protocols that a Mac's USB-C port can offer are USB, Thunderbolt, &amp;amp; DisplayPort. But there is still a lot of confusing detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  DisplayPort: USB v Thunderbolt
&lt;/h2&gt;

&lt;p&gt;Because the connector is the same, we have to be aware of the differences in the &lt;em&gt;protocols&lt;/em&gt;. Most Macs provide &lt;a href="https://en.wikipedia.org/wiki/Thunderbolt_(interface)" rel="noopener noreferrer"&gt;Thunderbolt&lt;/a&gt;, but they support the DisplayPort Alt-mode over USB as well. And there are different rules for the two protocols, even if the port is the same.&lt;/p&gt;

&lt;p&gt;Apple has an article to help you &lt;a href="https://support.apple.com/en-us/109523" rel="noopener noreferrer"&gt;Identify the ports on your Mac&lt;/a&gt; so you know which standards your Mac uses. Be aware that while all Apple Silicon Macs offer Thunderbolt, &lt;em&gt;not on all of the USB-C ports&lt;/em&gt; on some models are Thunderbolt, especially the front ports on the Mac mini and &lt;a href="https://www.apple.com/mac-studio/specs/" rel="noopener noreferrer"&gt;Mac Studio...depending on processor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just to make things confusing, &lt;a href="https://www.cablematters.com/Blog/Thunderbolt/usb4-vs-thunderbolt-3" rel="noopener noreferrer"&gt;the USB4 spec contains support for Thunderbolt 3/4&lt;/a&gt;, so USB4 devices &lt;em&gt;may&lt;/em&gt; support Thunderbolt &lt;em&gt;but they don't have to&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It really is complicated.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Chaining and Splitting
&lt;/h3&gt;

&lt;p&gt;Thunderbolt mostly uses &lt;em&gt;daisy-chaining&lt;/em&gt;, or connecting devices in series. Most Macs with Thunderbolt 3 or later have the ability to support two displays on one port, which is ideal for docking stations. There are limitations on this, so please reference the Apple Support articles above for your specific computer. I have not personally confirmed this, but it seems in &lt;a href="https://forums.macrumors.com/threads/daisy-chaining-one-thunderbolt-display-and-one-displayport-display.2369722/" rel="noopener noreferrer"&gt;some&lt;/a&gt; &lt;a href="https://www.reddit.com/r/UsbCHardware/comments/sdgpax/need_help_understanding_daisy_chaining_with/" rel="noopener noreferrer"&gt;cases&lt;/a&gt; you can chain a DisplayPort monitor from a Thunderbolt monitor. This was not the case with the Apple Thunderbolt Display (using Thunderbolt 2 over a Mini-DisplayPort connector), and does depend on the Thunderbolt controller of the first display.&lt;/p&gt;

&lt;p&gt;On devices which support chaining multiple monitors &lt;em&gt;over Thunderbolt&lt;/em&gt;, at least the first monitor must have two Thunderbolt ports. Such monitors are usually from the professional display market and are often much more expensive than their DisplayPort counterparts – a quick search found &lt;a href="https://www.benq.com/en-us/monitor/professional/pd2725u.html" rel="noopener noreferrer"&gt;only one for under $1000 USD&lt;/a&gt; – but they do exist.&lt;/p&gt;

&lt;p&gt;Most of the time, though, monitors with a USB-C port are offering DisplayPort Alt-mode over USB. Macs support this, but with only one display per port – more accurately &lt;em&gt;one display per chain&lt;/em&gt;*. This is where many of the headaches happen when people try to use a dock for one-cable convenience.&lt;/p&gt;

&lt;p&gt;* I say one display per chain because you &lt;em&gt;might&lt;/em&gt; be able to run two DisplayPort adapters off a USB-C hub or dock. I have not been able to find a reputable source for this, though. Most of the examples of this are using one of the solutions further down this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bandwidth Bummers
&lt;/h2&gt;

&lt;p&gt;When we start getting into specific docks, we can also run into the issue of supported bandwidth. The number of pixels each screen has (resolution), and the number of times per second they update (refresh rate) adds up to certain bandwidth limits. These concerns are more rare with modern docks, but Apple's tendency toward "Retina" high-resolution (generically called HiDPI) can make this a possible concern.&lt;/p&gt;

&lt;p&gt;This is why the descriptions in the Display Support section on the &lt;a href="https://www.apple.com/macbook-pro/specs/" rel="noopener noreferrer"&gt;Mac Tech Specs&lt;/a&gt; have so much detail:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;M4 Max&lt;br&gt;
Simultaneously supports full native resolution on the built-in display at 1 billion colors and:&lt;br&gt;
Up to four external displays: Up to three external displays with 6K resolution at 60Hz over Thunderbolt and one external display with up to 4K resolution at 144Hz over HDMI&lt;br&gt;
Up to three external displays: Up to two external displays with 6K resolution at 60Hz over Thunderbolt and one external display with up to 8K resolution at 60Hz or one external display with 4K resolution at 240Hz over HDMI&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your Mac will output the preferred resolution of the monitor, no matter what you select in the Displays screen; even choosing "low resolution" modes. Older macOS versions supported disabling HiDPI (Retina) modes, but those commands do not appear to work on more recent versions macOS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccut7n0vd7tnygsf7wup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccut7n0vd7tnygsf7wup.png" alt="macOS Settings: Display Resolution List" width="800" height="1420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So while there are some limits within the Mac itself, there may be lower limits on the docks or devices you use.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.dell.com/community/en/conversations/latitude/demystifying-dell-wd19tb-supported-display-resolutions/647f80cff4ccf8a8def80bad" rel="noopener noreferrer"&gt;Dell Community Forum thread from 2019&lt;/a&gt; does an excellent job of explaining some of these details on the Dell WD19TB, where the internal design of the dock may be a limiting factor. I use 4k displays. Even when I select a scaled resolution, the Mac outputs the high resolution signal. This may have prevented me from testing whether I could get three separate displays working on my WD19TB simply because 4K requires more bandwidth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Stream Transport
&lt;/h2&gt;

&lt;p&gt;Some docks – and monitors – support &lt;a href="https://en.wikipedia.org/wiki/DisplayPort#Multiple_displays_on_single_DisplayPort_connector" rel="noopener noreferrer"&gt;DisplayPort Multi-Stream Transport&lt;/a&gt; which allow you to either daisy-chain multiple monitors together or use a dock that splits a single DisplayPort channel to multiple separate displays. This is &lt;a href="https://www.dell.com/support/contents/en-us/article/product-support/self-support-knowledgebase/monitor-screen-video/guide-daisy-chain-monitors" rel="noopener noreferrer"&gt;fairly common in the PC world&lt;/a&gt;, and you can even find &lt;a href="https://www.startech.com/en-eu/display-video-adapters/mstdp124dp" rel="noopener noreferrer"&gt;inexpensive devices that support four monitors on Windows&lt;/a&gt;, but it is &lt;strong&gt;completely unsupported by macOS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Apple has never supported DisplayPort MST in the fifteen years it has been available, even when it was usable on Intel-based Macintosh hardware running Windows. As I understand it, &lt;a href="https://asahilinux.org/docs/hw/soc/display-controllers/" rel="noopener noreferrer"&gt;Apple Silicon display controllers do not include MST support&lt;/a&gt; at all, so MST support cannot be added to current devices.&lt;/p&gt;

&lt;p&gt;If you have a dual-monitor dock or a chain of monitors that works on Windows using DisplayPort MST, it will not work on macOS. Depending on the design, it may allow you to use one external monitor or mirror the same output on both displays, but there seem to be cases where multi-monitor docks do not work at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  MST Documentation Problems
&lt;/h3&gt;

&lt;p&gt;Awareness of MST is especially important if you are looking at docks originally focused on the Windows market. Most of the Windows world assumes MST support is available and &lt;em&gt;it may not even be mentioned&lt;/em&gt; in the user guide. This inconsistency is one of the biggest risks as a Mac user looking for docks with multi monitor support.&lt;/p&gt;

&lt;p&gt;I use a Dell Thunderbolt WD19TB dock for work. It drives my two external 4k displays – one over Thunderbolt and one over HDMI. The dock is advertised as supporting &lt;em&gt;four&lt;/em&gt; displays, as &lt;a href="https://www.dell.com/support/manuals/en-us/dell-wd19tb-dock/WD19_TB_UserGuide/display-resolution-table?guid=guid-5558fc88-5c31-4e2c-90be-42359855a1ae&amp;amp;lang=en-us" rel="noopener noreferrer"&gt;the WD19TB manual&lt;/a&gt; indicates. After playing around with different ports and resolutions, I was able to get &lt;em&gt;three external displays&lt;/em&gt; working, but only with mirroring the DisplayPort-over-USB-C to the second regular DisplayPort. The third (mirrored) monitor does not appear in the OS at all. This is a pretty common symptom of MST.&lt;/p&gt;

&lt;p&gt;Despite no mention of MST or Multi-Stream Transport in the product specs or even the User Guide, a table in &lt;a href="https://dl.dell.com/topicspdf/dell-wd19tb-dock_administrator-guide_en-us.pdf" rel="noopener noreferrer"&gt;the W19TB Administrator's Guide&lt;/a&gt; section about firmware update timings mentions MST. &lt;/p&gt;

&lt;h2&gt;
  
  
  Thunderbolt Docks
&lt;/h2&gt;

&lt;p&gt;There have been a variety of Thunderbolt docks available or years, but a Thunderbolt dock does not mean it is free of complexity. The afore-mentioned Dell WD19TB is one such, where despite Thunderbolt support it uses MST for some of the display connectors and has internal bandwidth limits.&lt;/p&gt;

&lt;p&gt;When selecting a dock, check for Macintosh support. Brands like &lt;a href="https://www.caldigit.com/docks/" rel="noopener noreferrer"&gt;CalDigit&lt;/a&gt; and especially &lt;a href="https://www.owc.com/solutions/connectivity?subcategory=thunderbolt-docks-hubs" rel="noopener noreferrer"&gt;OWC&lt;/a&gt; are known for working with Macs.&lt;/p&gt;

&lt;h2&gt;
  
  
  DisplayLink Docks
&lt;/h2&gt;

&lt;p&gt;An option that appeared well before MST and Thunderbolt is &lt;a href="https://displaylink.com" rel="noopener noreferrer"&gt;DisplayLink&lt;/a&gt;. Using software to create virtual displays and stream that information over USB, DisplayLink works around lacking hardware support.&lt;/p&gt;

&lt;p&gt;DisplayLink requires a software driver to be installed on the operating system. It is widely supported on Mac, Windows &amp;amp; Linux, but it isn't plug-and-play. If you are on a work-managed device, you may need permissions to install the DisplayLink driver.&lt;/p&gt;

&lt;p&gt;Some people have had negative experiences with DisplayLink, but that has not been the case for me. If you have a Mac with limited support for external monitors, it might be a useful alternative.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not Without Limits
&lt;/h3&gt;

&lt;p&gt;DisplayLink is an acceptable &lt;em&gt;general computing&lt;/em&gt; solution. It uses software to simulate display support, meaning it leverages your processor to do the work. For most casual computer use – word processing, code editing, browsing the web – this may be a viable choice.&lt;/p&gt;

&lt;p&gt;What it doesn't do well is motion, either gaming or video. The DisplayLink software layer causes increased lag to the display, and uses various compression methods which may limit the image quality.&lt;/p&gt;

&lt;p&gt;While some of the DisplayLink systems do support higher frame rates, you should not expect it from these devices in general. DisplayLink also may have issues with DRM – Digital Rights Management – which is required to play most video at 1080p or higher quality. This means you may get a black box trying to view most video content on a DisplayLink monitor. Some users have found that disabling hardware acceleration in their browser resolves the issue. If you are able to stream content on DisplayLink, the image quality may be degraded by the limits of software processing and the USB bandwidth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://displaylink.org/forum/showthread.php?t=67427" rel="noopener noreferrer"&gt;A 2020 post to the DisplayLink forums&lt;/a&gt; includes a response from a member of the support team clarifying that color profiles and some software display controls are not supported through DisplayLink. If color accuracy is important, this is probably not the solution you are looking for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identification
&lt;/h3&gt;

&lt;p&gt;It may be difficult to distinguish an MST dock from a DisplayLink dock when comparing or shopping, but the specs &lt;em&gt;usually&lt;/em&gt; mention DisplayLink. Multi-monitor docks that provide no information on their product pages are likely using MST.&lt;/p&gt;

&lt;p&gt;As an example, the &lt;a href="https://us.targus.com/collections/usb-c-alt-mode-docks/products/usb-c-displayport-alt-mode-docking-station-85w-power-dock430usz" rel="noopener noreferrer"&gt;Targus DOCK430USZ&lt;/a&gt; makes no mention of either MST or DisplayLink on the product page. After locating the &lt;a href="https://cdn.targus.com/web/us/downloads/DOCK430USZ-50-A_UG_041619.pdf" rel="noopener noreferrer"&gt;PDF Manual&lt;/a&gt; there's a small blurb that clarified it uses MST (complete with spelling error):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Graphic adapter supporting Multi-Stream&lt;br&gt;
Transport (MST) Mode is reuqired for dual or tri monitor&lt;br&gt;
display mode&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Other Software-Required Alternatives
&lt;/h3&gt;

&lt;p&gt;There are even more alternatives to the alternative. The less-known multi-monitor solution called &lt;a href="https://www.siliconmotion.com/events/instantview/" rel="noopener noreferrer"&gt;InstantView by SiliconMotion&lt;/a&gt;, and another from &lt;a href="https://en.j5create.com/collections/full-featured-docking-stations/products/jcd543p" rel="noopener noreferrer"&gt;j5create&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;InstantView doesn't require installing a DisplayLink driver, you do have to run the InstantView software and grant it permissions. There are a handful of USB hubs from Hyper and Anker that supports a second monitor through "no install InstantView" software. It's still loading the second display driver in software, and using some OS tricks to do it. Experiences vary with performance, resolution issues, color inconsistency, and lag. &lt;a href="https://www.reddit.com/r/mac/comments/1bgvh16/i_used_both_displaylink_and_siliconmotion/" rel="noopener noreferrer"&gt;A Reddit post to r/Mac comparing them in 2024&lt;/a&gt; was not very complimentary, though. &lt;/p&gt;

&lt;p&gt;j5create seems to use their own separate hardware and driver combination. Information was more limited on these products.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docks vs. Hubs
&lt;/h2&gt;

&lt;p&gt;We've identified a number of considerations for &lt;em&gt;docks&lt;/em&gt;, but we need to talk about &lt;em&gt;hubs&lt;/em&gt;. Unlike docks, which provide a variety of ports, hubs are &lt;em&gt;almost exclusively&lt;/em&gt; focused on providing more of a specific type of port. This is the case with simple USB Hubs – one USB in, two or more USB ports out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.startech.com/en-eu/usb-hubs/hb31c3a1cdppd3" rel="noopener noreferrer"&gt;Some USB-only hubs support USB-C DisplayPort Alt-mode&lt;/a&gt;, allowing you access to regular USB ports without preventing you from using the USB-C capabilities. Hubs like these might be good for on-the-go use, but they are still limited to one DisplayPort output per port to the best of my ability to find information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thunderbolt Hubs
&lt;/h3&gt;

&lt;p&gt;Thunderbolt standardized on the USB-C connector, and USB4 is essentially Thunderbolt 3. Thunderbolt 4 and 5 are mostly incremental bandwidth improvements. While actual Thunderbolt docks have been around for some time – I used a Thunderbolt 2 dock with my 2013 MacBook Air – many of these used Thunderbolt but did not provide multiple Thunderbolt ports for more devices. There are now true Thunderbolt Hubs which allow you to add more Thunderbolt devices without daisy-chaining. This opens up possibilities both for devices and displays.&lt;/p&gt;

&lt;p&gt;A single Thunderbolt port on most Macs these days does support two displays – only one if it is an 8K monitor or using high refresh rates – and Thunderbolt Hubs provide support for this.&lt;/p&gt;

&lt;p&gt;I haven't used one of these devices, but something like &lt;a href="https://www.owc.com/solutions/thunderbolt-5-hub" rel="noopener noreferrer"&gt;OWC's Thunderbolt 5 Hub&lt;/a&gt; is  definitely on my radar for my next desk update. I have seen a &lt;a href="https://www.youtube.com/watch?v=Pvkff5NWEVs" rel="noopener noreferrer"&gt;video demonstrating three displays on the Thunderbolt 5 Hub&lt;/a&gt;. Still, I have not tried it, personally. OWC does also provide their own &lt;a href="https://eshop.macsales.com/manuals/apple-silicon-chip-display-support-support-article?srsltid=AfmBOoonzU2DK_XpPaeK3iNQUCOffWBz8LNlW0tzNYJ9sOvTwMgCd5xY" rel="noopener noreferrer"&gt;Apple Silicon External Display support article&lt;/a&gt; to attempt to clarify display support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Articles
&lt;/h2&gt;

&lt;p&gt;This is such a popular problem that while I've been writing this article, &lt;a href="https://www.macworld.com/article/675869/how-to-connect-two-or-more-external-displays-to-apple-silicon-m1-macs.html" rel="noopener noreferrer"&gt;Macworld came out with an article of their own&lt;/a&gt; on this topic. They suggest primarily DisplayLink options, though, so take that as you will.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are honestly too many things to keep in mind when trying to determine exactly how to connect a specific Mac to more than one external display. Much depends on your setup and use cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many displays do you want to connect?&lt;/li&gt;
&lt;li&gt;What resolution are those displays?&lt;/li&gt;
&lt;li&gt;Will you use the computer with the lid closed?&lt;/li&gt;
&lt;li&gt;How many cables are you willing to put up with?&lt;/li&gt;
&lt;li&gt;Are the trade-offs of software-based solutions acceptable?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There aren't any universal answers to these problems, other than saying, "Do your homework." I hope I've provided some sources and starting points to help you on your multi-monitor journey.&lt;/p&gt;

&lt;p&gt;If you have some wisdom or experience to share, or if you spot a mistake, please comment and share! Thanks!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>mac</category>
      <category>apple</category>
    </item>
    <item>
      <title>useCallback is a Lie!</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Thu, 14 Aug 2025 19:40:10 +0000</pubDate>
      <link>https://forem.com/oculus42/usecallback-is-a-lie-3hji</link>
      <guid>https://forem.com/oculus42/usecallback-is-a-lie-3hji</guid>
      <description>&lt;p&gt;Well, not exactly, but it may not work quite how you expect. It didn't work how &lt;em&gt;I&lt;/em&gt; expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functions Not Required
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://react.dev/reference/react/useCallback" rel="noopener noreferrer"&gt;The useCallback docs&lt;/a&gt; provide a couple of interesting clues (emphasis mine). It's a big block of text, but it's all relevant.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;fn&lt;/code&gt;: The function &lt;strong&gt;value that you want to cache&lt;/strong&gt;. It can take any arguments and return any values. &lt;strong&gt;React will return (not call!) your function&lt;/strong&gt; back to you during the initial render. On next renders, React will give you the same function again if the dependencies have not changed since the last render. Otherwise, it will give you the function that you have passed during the current render, and store it in case it can be reused later. &lt;strong&gt;React will not call your function.&lt;/strong&gt; The function is returned to you so you can decide when and whether to call it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's break that down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useCallback&lt;/code&gt; receives a (function) &lt;strong&gt;value&lt;/strong&gt; and a dependency array.&lt;/li&gt;
&lt;li&gt;It caches and returns that (function) &lt;strong&gt;value&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Whenever the calling function is re-run it returns the original (function) &lt;strong&gt;value&lt;/strong&gt; unless the dependencies array changes.&lt;/li&gt;
&lt;li&gt;Whenever the dependencies change, it will store and return that new (function) &lt;strong&gt;value&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hooks, Closures, and Stale Values
&lt;/h3&gt;

&lt;p&gt;This is really important for functions in React. Functions have access to the scope in which they were created (closures!), including the values of props and other hooks. Most hooks return immutable values, so if the props or other hooks change, the closure in which the callback was created &lt;em&gt;becomes stale&lt;/em&gt;. It will still have old values if we call the function. We need to let go of the copy with a stale closure and get a fresh one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call Me, Maybe?
&lt;/h3&gt;

&lt;p&gt;The docs say &lt;em&gt;twice&lt;/em&gt; that &lt;code&gt;useCallback&lt;/code&gt; doesn't call our function. It just holds onto it for us so we can call it later if we ever need to. And the dependencies are there in case our &lt;del&gt;phone number changes&lt;/del&gt; scope changes and we need a fresh version with fresh scope.&lt;/p&gt;

&lt;p&gt;But since React never calls the function, it doesn't matter if it's a function! &lt;strong&gt;React is just storing a value.&lt;/strong&gt; We've just agreed by naming convention that value will be a function. &lt;em&gt;But it doesn't have to be&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  First Class Functions FTW
&lt;/h3&gt;

&lt;p&gt;JavaScript lets us store functions as values and pass them as arguments. That's exactly what this higher-order &lt;code&gt;useCallback&lt;/code&gt; function expects us to do. But we can pass a number, an object, a Promise, or anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// No dependencies, so it never changes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;something&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;another&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;something&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;something&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;another&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cough SIGNALS Cough
&lt;/h3&gt;

&lt;p&gt;Incidentally, signals return a function rather than a value, where the function is a consistent return and the signal manages the scope internally rather than the component. This is why signals &lt;del&gt;rule&lt;/del&gt; don't require manual dependency tracking and do not experience stale values.&lt;/p&gt;

&lt;p&gt;Just sayin'.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simpler &lt;code&gt;useEffect&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;If you are hanging onto a simple object or computation, it might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useEffect &amp;amp; useState Edition&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Nested&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, probably better if you use &lt;code&gt;useMemo&lt;/code&gt; for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useMemo Edition&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Nested&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But you could just pass that directly to &lt;code&gt;useCallback&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useCallback Edition&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Nested&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;const useMemoValue = useCallback;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;What we really have here is a function that accepts a value and will hold onto it until there is a change in the dependencies array. No types. No function executions. Nothing. Just holding onto some value.&lt;/p&gt;

&lt;p&gt;You could even use this as a slightly sketchy &lt;code&gt;useRef&lt;/code&gt; alternative, where you can keep and mutate an object until a dependency forces you to update it. If you have a &lt;code&gt;useRef&lt;/code&gt; that gets updated in a &lt;code&gt;useEffect&lt;/code&gt;, &lt;em&gt;you might&lt;/em&gt; be able to merge those two operations with a misused &lt;code&gt;useCallback&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But you probably shouldn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Still Want &lt;code&gt;useMemo&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Except in rare cases like the example above where the operation is trivial, the difference between misusing &lt;code&gt;useCallback&lt;/code&gt; and correctly using &lt;code&gt;useMemo&lt;/code&gt; is that &lt;code&gt;useMemo&lt;/code&gt; doesn't do any of the work in the function you pass it until the props change. Whatever is passed to &lt;code&gt;useCallback&lt;/code&gt;, it will do that work &lt;em&gt;every time&lt;/em&gt; it renders the component. With &lt;code&gt;useMemo&lt;/code&gt; the work is creating a function, not executing the contents of that function.&lt;/p&gt;

&lt;p&gt;Simple things like restructuring a single object in our example work well with &lt;code&gt;useCallback&lt;/code&gt;, but processing an array is a bad choice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Only executes the `.map()` when the dependencies change&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;good&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bigData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;complexTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;someState&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;bigData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;someState&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Runs the `.map()` every time the component is rendered&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;questionable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;bigData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;complexTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;someState&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;bigData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;someState&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be honest, trying to use &lt;code&gt;useCallback&lt;/code&gt; this way is probably a bad choice in general.&lt;/p&gt;

&lt;h3&gt;
  
  
  Convention is Communication
&lt;/h3&gt;

&lt;p&gt;As the code author, you might understand that &lt;code&gt;useCallback&lt;/code&gt; doesn't have to store a callback function and has this "off label" use. However, other developers may not. Even future you would likely pause at that code and wonder if you meant to use a different hook. You would probably end up adding a comment to clarify the usage, and justify your decision to other developers or to your future self.&lt;/p&gt;

&lt;p&gt;All of that means that anyone reading that code will have to stop whatever analysis they are doing to read the comment and understand why &lt;em&gt;this obviously wrong code&lt;/em&gt; is not actually wrong and isn't part of the problem they're looking for...unless of course it is, because in doing something non-standard we created some other unexpected defect.&lt;/p&gt;

&lt;p&gt;So you really do want &lt;code&gt;useMemo&lt;/code&gt;. It does what we expect. It does what the next developer expects. It doesn't require extra mental overhead to understand or explain itself.&lt;/p&gt;

&lt;p&gt;Consistency provides a lot of benefit. When we decide to write "interesting" or &lt;em&gt;clever&lt;/em&gt; code, it comes at the cost of maintainability and future efficiency. Sometimes it's worth it, but often it isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;So, &lt;code&gt;useCallback&lt;/code&gt; is a lie. It's not about the callback; it's about the &lt;em&gt;closure&lt;/em&gt;. It's good to know this is the case, because it will help you understand why omitting dependencies can cause problems, but it's also good to know not to use it in this way unless you have a &lt;em&gt;really good reason&lt;/em&gt; to do so.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Use Slice, not Substring</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Thu, 17 Jul 2025 03:51:32 +0000</pubDate>
      <link>https://forem.com/oculus42/use-slice-not-substring-javascript-quick-bits-2ci</link>
      <guid>https://forem.com/oculus42/use-slice-not-substring-javascript-quick-bits-2ci</guid>
      <description>&lt;p&gt;JavaScript &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring" rel="noopener noreferrer"&gt;&lt;code&gt;String.prototype.substring()&lt;/code&gt;&lt;/a&gt; and it's confusingly similar yet deprecated cousin &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr" rel="noopener noreferrer"&gt;&lt;code&gt;.substr()&lt;/code&gt;&lt;/a&gt; have been around a long time, but so has the better solution: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice" rel="noopener noreferrer"&gt;&lt;code&gt;.slice()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Slice is most compatible with modern JavaScript. It accepts one or two indices, supports negative indices, and operates predictably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Of Indexes and Indices
&lt;/h2&gt;

&lt;p&gt;Indexes and indices are interchangeable plurals for index. Databases usually use indexes to refer to optimized lookup tables. Array operations usually refers to indices. Math often uses indices and while much of programming follows from math, there is a mix. The MDN article on &lt;code&gt;substring&lt;/code&gt; uses both, for instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Substr: Right Out
&lt;/h2&gt;

&lt;p&gt;Substr is deprecated. It was never part of the core spec, and unlike most other string operations it takes &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;length&lt;/code&gt;. I've added it to the example comparison because it lives in many older codebases, but it's not recommended.&lt;/p&gt;

&lt;p&gt;Honestly, &lt;code&gt;substr&lt;/code&gt; is "closer to the metal", meaning it's more closely aligned with the computer hardware than the conceptual logic being executed. &lt;a href="https://pubs.opengroup.org/onlinepubs/009696899/functions/strncpy.html" rel="noopener noreferrer"&gt;The &lt;code&gt;strncopy&lt;/code&gt; method in  C&lt;/a&gt; accepts a &lt;code&gt;size&lt;/code&gt; similar to the &lt;code&gt;length&lt;/code&gt; parameter. This is &lt;a href="http://www.danielvik.com/2010/02/fast-memcpy-in-c.html" rel="noopener noreferrer"&gt;essentially replicating how memory is copied at the hardware level&lt;/a&gt;; a starting point and a number of bytes.&lt;/p&gt;

&lt;p&gt;When we work in applications, we are often less concerned with how the underlying hardware will handle a task. We might need to find the start and end, and have to perform math to know how many characters to copy. So we let the computer do that math for us, and just provide the indices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Substring: Just Trying to Help
&lt;/h2&gt;

&lt;p&gt;Substring has two "helpful" quirks to be aware of: clamped values and index swapping. These &lt;em&gt;aren't bad&lt;/em&gt;, but can have unexpected results if you aren't aware of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameter Swapping
&lt;/h3&gt;

&lt;p&gt;Let's say you have a little program that calculates the start and end of a substring you need to extract, and you get back two indices: &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt;. Sadly, there's a simple error in your logic, and the &lt;code&gt;end&lt;/code&gt; is smaller than the &lt;code&gt;start&lt;/code&gt; value! As explained in the &lt;code&gt;substr&lt;/code&gt; section above, the low-level logic is converting the pair of indices to a &lt;code&gt;size&lt;/code&gt; for string copies. Subtracting the indices would give us a negative number, which doesn't work as a length.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;substring&lt;/code&gt; assumes you made a mistake, and swaps the order for you! No matter how you pass your arguments, it will figure out which is lower, and put it first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'cd'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In most places this would either error, or return an empty string. The latter is what &lt;code&gt;.slice()&lt;/code&gt; does.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Substrings parameter swapping might seem like a helpful choice, but it has the possibility to hurt more than help. If our logic is wrong, it can return values that could confuse or delay identifying the defect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clamped Values
&lt;/h3&gt;

&lt;p&gt;Substring doesn't support negative numbers. Many of the more recent JavaScript additions that accept indices allow you to provide negative numbers as a representation of "from the end", where positive numbers are "from the beginning".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// 'a'&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'e'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Substring assumes &lt;em&gt;you must have been mistaken&lt;/em&gt;, and clamps values to zero if they are negative. You can't start before zero, right? That's why &lt;code&gt;indexOf&lt;/code&gt; returns &lt;code&gt;-1&lt;/code&gt; if something is not found. So &lt;code&gt;substring&lt;/code&gt; won't go below zero.&lt;/p&gt;

&lt;p&gt;Now, all of the methods will convert &lt;em&gt;non-numeric&lt;/em&gt; values to zero, but substring not supporting negatives means it works differently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bad values&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'abc'&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'abc'&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// 'abc'&lt;/span&gt;

&lt;span class="c1"&gt;// Negative values&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'abcde'&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// 'de'&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// 'de'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It gets especially complicated if you thought you could use negative indices to get several characters near the end of a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// 'cd'&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abcde&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ''&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And mixing positive and negative numbers is even worse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!Hola¡&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// 'Hola'&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!Hola¡&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// '!'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Substring clamps this as &lt;code&gt;(1, 0)&lt;/code&gt;, and then swaps the order for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;Here's a small chart of different inputs and what the functions produce. This is all run against a string that contains the alphabet.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Args&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;&lt;code&gt;substring&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;&lt;code&gt;substr&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;&lt;code&gt;slice&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fn(20)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;'uvwxyz'&lt;/td&gt;
&lt;td&gt;'uvwxyz'&lt;/td&gt;
&lt;td&gt;'uvwxyz'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fn(-5)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;'abcdefghijklmnopqrstuvwxyz'&lt;/td&gt;
&lt;td&gt;'vwxyz'&lt;/td&gt;
&lt;td&gt;'vwxyz'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fn(5, 10)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;'fghij'&lt;/td&gt;
&lt;td&gt;'fghijklmno'&lt;/td&gt;
&lt;td&gt;'fghij'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fn(5, -5)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;'abcde'&lt;/td&gt;
&lt;td&gt;''&lt;/td&gt;
&lt;td&gt;'fghijklmnopqrstu'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fn(-10, -5)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;''&lt;/td&gt;
&lt;td&gt;''&lt;/td&gt;
&lt;td&gt;'qrstu'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fn(-5, -10)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;''&lt;/td&gt;
&lt;td&gt;''&lt;/td&gt;
&lt;td&gt;''&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fn(10, 5)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;'fghij'&lt;/td&gt;
&lt;td&gt;'klmno'&lt;/td&gt;
&lt;td&gt;''&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last two entries return an empty string for &lt;code&gt;.slice()&lt;/code&gt;, but they are the "incorrect" arguments where the index of the second argument is lower than the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;substring&lt;/code&gt; has some unusual conditions that could be helpful but are incompatible with more modern use of negative indices. &lt;code&gt;substr&lt;/code&gt; is deprecated. &lt;code&gt;slice&lt;/code&gt; is where you want to be.&lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Brevity At Work</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Tue, 08 Jul 2025 22:25:22 +0000</pubDate>
      <link>https://forem.com/oculus42/brevity-2ljp</link>
      <guid>https://forem.com/oculus42/brevity-2ljp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;It seems that perfection is attained not when there is nothing more to add, but when there is nothing more to remove.&lt;br&gt;
  — Antoine de Saint-Exupéry, translated from &lt;em&gt;Terre des hommes&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Core Principles
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Get to the Point
&lt;/h3&gt;

&lt;p&gt;We talk too much. We send long emails. We restate and repeat and ramble. It's hard to learn to stop talking, but we can do things differently. We just need tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get Confirmation
&lt;/h3&gt;

&lt;p&gt;Over-explaining prevents misunderstandings, but wastes time. Confirmation to the rescue!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask before giving a long explanation.&lt;/li&gt;
&lt;li&gt;Pause frequently to confirm if others understand.&lt;/li&gt;
&lt;li&gt;Check if you need to continue.&lt;/li&gt;
&lt;li&gt;Don't assume.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Know Your Audience
&lt;/h3&gt;

&lt;p&gt;Being very concise may seem rude to some. Expectations can vary by interaction and culture. We avoid interrupting customers or managers because of policy or power dynamics.&lt;/p&gt;

&lt;p&gt;What does your audience need to know? What is their motivation? Do you need their awareness, interest, or action?&lt;/p&gt;

&lt;p&gt;Think like a storyteller.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remove everything that has no relevance to the story. If you say in the first chapter that there is a rifle hanging on the wall, in the second or third chapter it absolutely must go off. If it’s not going to be fired, it shouldn’t be hanging there.&lt;br&gt;
— Anton Chekhov&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Consider the goals of the communication.&lt;/li&gt;
&lt;li&gt;Give the short summary and offer to provide more information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Finding Balance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Taking it Too Far
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faeea9menenfg3ijkcd24.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faeea9menenfg3ijkcd24.jpg" alt=" " width="700" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Too little information is also bad. Newer team members may not know things. New projects may need explanation. Not everyone can do research before a meeting. Some detail is necessary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't expect others to know everything you do.&lt;/li&gt;
&lt;li&gt;Don't omit detail without asking for confirmation of understanding.&lt;/li&gt;
&lt;li&gt;Don't be rude if someone does not understand.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If it is not working, try more detail. Call out the change so others are aware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cutting People Off
&lt;/h3&gt;

&lt;p&gt;Sometimes you must stop someone from carrying on. This is difficult and can cause hurt feelings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid rejecting the information presented.&lt;/li&gt;
&lt;li&gt;Provide a reason for the interruption.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"Sorry to cut you off, but there's a hand raised we need to address."&lt;/p&gt;

&lt;p&gt;"I'd like to bring us back to the high-level discussion for now."&lt;/p&gt;

&lt;p&gt;"This is good information, but let's stay focused on this."&lt;/p&gt;

&lt;p&gt;"Let's add this to the parking lot." &lt;/p&gt;

&lt;p&gt;Be humble. Sometimes others may need to interrupt you, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools and Channels
&lt;/h2&gt;

&lt;p&gt;Different interactions require different techniques.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When defining the problem, get frequent confirmation.&lt;/li&gt;
&lt;li&gt;For follow-up meetings, don't restate the whole problem.&lt;/li&gt;
&lt;li&gt;For status calls, summarize, don't narrate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Concise Communication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Bullet-point lists can be referenced more reliably than paragraphs.&lt;/li&gt;
&lt;li&gt;Add an executive summary to save time if readers don't need detail.&lt;/li&gt;
&lt;li&gt;Call out participants of relevant sections in meetings and messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Writing
&lt;/h3&gt;

&lt;p&gt;Writing tools like the &lt;a href="https://hemingwayapp.com" rel="noopener noreferrer"&gt;Hemingway Editor&lt;/a&gt; or &lt;a href="https://www.grammarly.com" rel="noopener noreferrer"&gt;Grammarly&lt;/a&gt; can help make your writing concise. AI tools like &lt;a href="https://chatgpt.com" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt;, &lt;a href="https://claude.ai/" rel="noopener noreferrer"&gt;Claude&lt;/a&gt;, and various iterations of the AI assistants available in note/documentation apps like &lt;a href="https://www.notion.com" rel="noopener noreferrer"&gt;Notion&lt;/a&gt; can do the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Digital Communication
&lt;/h3&gt;

&lt;p&gt;Email is all about prioritization. Titles set the tone. They should be clear and distinct.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid long titles. They may be cut off on some screens.&lt;/li&gt;
&lt;li&gt;Avoid questions or links in titles. Those are details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;No:&lt;/strong&gt; "Questions about Next Year's Upcoming Project TICKET-1234"&lt;br&gt;
&lt;strong&gt;Yes:&lt;/strong&gt; "TICKET-1234 - Questions"&lt;/p&gt;

&lt;p&gt;For one-on-one or small group chats, try the &lt;a href="https://nohello.net/en/" rel="noopener noreferrer"&gt;no hello&lt;/a&gt; method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meetings
&lt;/h3&gt;

&lt;p&gt;Many meetings styles identify different efficiency tools.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set an agenda.&lt;/li&gt;
&lt;li&gt;Set expectations for communication, or ask if none are provided.&lt;/li&gt;
&lt;li&gt;Use a "parking lot" of questions &amp;amp; topics to avoid getting distracted from the agenda.&lt;/li&gt;
&lt;li&gt;Use raised hands to interrupt long responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Long meetings are difficult. You cannot keep everyone's full attention indefinitely. Act accordingly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call out a respondent's name &lt;em&gt;before asking a question&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Ask for everyone's attention when you need it.&lt;/li&gt;
&lt;li&gt;Use raised hands to confirm people are attentive if needed.&lt;/li&gt;
&lt;li&gt;Let people know when "all focus" time ends.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Culture of Learning
&lt;/h2&gt;

&lt;p&gt;We all have different things to learn. If people can say, "I don't know," they can ask for clarity when they need it. Management must consistently support this, or employees will stop asking. If they stop asking, we have to explain &lt;em&gt;just in case&lt;/em&gt;, and we are back to wasting time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reinforce a learning culture.&lt;/li&gt;
&lt;li&gt;Express appreciation when someone says, "I don't know."&lt;/li&gt;
&lt;li&gt;Never embarrass or belittle someone for not knowing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a free pass for team members to be lazy, but understanding that we all know different things is important, and sharing knowledge is critical to everyone's success.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://xkcd.com/1053/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5yj86fouy86y69uhxgfg.png" alt=" " width="800" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Be brief.&lt;/li&gt;
&lt;li&gt;Get Confirmation.&lt;/li&gt;
&lt;li&gt;Know your audience.&lt;/li&gt;
&lt;li&gt;Don't overdo it.&lt;/li&gt;
&lt;li&gt;Encourage "I don't know."&lt;/li&gt;
&lt;li&gt;Foster learning.&lt;/li&gt;
&lt;li&gt;Respect people's time and attention.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Say what you need to say.&lt;br&gt;
  — John Mayer&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  More to Say?
&lt;/h2&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>communication</category>
    </item>
    <item>
      <title>Bad Interview, Good Practice</title>
      <dc:creator>Samuel Rouse</dc:creator>
      <pubDate>Fri, 20 Jun 2025 17:53:30 +0000</pubDate>
      <link>https://forem.com/oculus42/bad-interview-good-practice-2pjm</link>
      <guid>https://forem.com/oculus42/bad-interview-good-practice-2pjm</guid>
      <description>&lt;p&gt;LeetCode, Codewars, and similar sites that "test your skill" as a developer aren't good interview tools, but they can be good &lt;em&gt;personal skill builders&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relevance vs Regurgitation
&lt;/h2&gt;

&lt;p&gt;Rather than highlighting developers who can follow direction and solve problems, using sites like these as skill-validation tools can strongly favor developers who memorized the answer rather than developers who can demonstrate skill in reading requirements, following instructions, and solving problems.&lt;/p&gt;

&lt;p&gt;Don't get me wrong; knowing the answer is an important part of development. Not reinventing the wheel saves us time, complexity, and risk. But there are different aspects of the role of developer, and &lt;em&gt;knowing about a linked list&lt;/em&gt; is different than demonstrating the ability to recreate one while racing a clock.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skill Builder
&lt;/h2&gt;

&lt;p&gt;As a &lt;em&gt;personal development tool&lt;/em&gt;, sites that offer challenges are great. Not only can you try something outside your usual experience, but you can often see hundreds of solutions and consider the benefits and problems with each. This can help you understand the problem space better, learn about style or language features you aren't familiar with, and reason about code that is very different than what you would typically produce. &lt;/p&gt;

&lt;h3&gt;
  
  
  Try New Things
&lt;/h3&gt;

&lt;p&gt;While trying different types of tasks can be helpful, these sites are also a great way to try &lt;em&gt;new coding styles&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Have you been meaning to practice more with JavaScript Array prototype methods instead of imperative for loops? What about seeing how compact you can make a given function?&lt;/p&gt;

&lt;h3&gt;
  
  
  Clever Coding
&lt;/h3&gt;

&lt;p&gt;Coding challenge sites are a great place to try &lt;em&gt;clever coding&lt;/em&gt; techniques, where you can accomplish something in a unique way. Maybe you can use an unexpected type conversion or bitwise XOR &lt;code&gt;^&lt;/code&gt; to shave characters off your solution.&lt;/p&gt;

&lt;p&gt;Clever coding is usually discouraged in business because it often trades readability and maintainability for efficiency or character count. Expecting every future developer to understand the particular tricks used and the full problem space adds unnecessary risk.&lt;/p&gt;

&lt;p&gt;But in a coding challenge site, it's a great time to try out unusual, risky, or just &lt;em&gt;different&lt;/em&gt; code. This can also help you develop the ability to read compiled or obfuscated code. It may be particularly helpful when looking at optimized library methods, which may choose to sacrifice readability for performance. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;These are useful pursuits: building familiarity, trying new language features, and practicing your skills in a low-risk environment. &lt;/p&gt;

&lt;p&gt;Interviewing is a difficult process, and it's easy to focus on the wrong details. Coding sites can be a good tool for a developer and provide a &lt;em&gt;shortcut&lt;/em&gt; when interviewing, but in over a decade of being an interviewer for enterprise developer roles, I've never relied on them because they often don't differentiate between practice and process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask the Audience: Skill Builder Sites?
&lt;/h2&gt;

&lt;p&gt;There are a number of different skill testing sites like these. I'm going to mention &lt;a href="https://alf.nu/RegexGolf?world=regex&amp;amp;level=r00" rel="noopener noreferrer"&gt;Regex Golf&lt;/a&gt;. Regex is difficult for a lot of people and learning how to solve problems in it can be very useful.&lt;/p&gt;

&lt;p&gt;What are your favorites?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
