<?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>DEV Community: Mat Jones</title>
    <description>The latest articles on DEV Community by Mat Jones (@matjones).</description>
    <link>https://dev.to/matjones</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%2F561062%2Ff3d74754-405b-4425-b408-2a4c06adefcb.png</url>
      <title>DEV Community: Mat Jones</title>
      <link>https://dev.to/matjones</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matjones"/>
    <language>en</language>
    <item>
      <title>Introducing Yaclt (Yet Another ChangeLog Tool)</title>
      <dc:creator>Mat Jones</dc:creator>
      <pubDate>Sun, 08 Aug 2021 20:08:07 +0000</pubDate>
      <link>https://dev.to/matjones/introducing-yaclt-yet-another-changelog-tool-574a</link>
      <guid>https://dev.to/matjones/introducing-yaclt-yet-another-changelog-tool-574a</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Several months ago, I set out to solve a pain point I was running into at work — manually writing release notes by copy and pasting entries from Merge Request descriptions. I couldn't find an existing tool that quite worked the way we wanted it to work. We didn't necessarily want to use git commit messages as our changelog entries, which eliminates a lot of the other changelog tools out there.&lt;/p&gt;

&lt;p&gt;We wanted to have explicit control over the changelog entries, and we wanted our developers to be intentional and thoughtful about the messages they add to the changelog. This manifested itself in the following workflow, supported by a set of bash scripts I set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each Merge Request contains a changelog entry in a single, self-contained markdown file within a &lt;code&gt;changelogs/&lt;/code&gt; directory (a file can contain multiple lines, each line being an individual changelog entry)

&lt;ul&gt;
&lt;li&gt;These changelogs are strictly validated against a format pattern&lt;/li&gt;
&lt;li&gt;This is enforced in a step of the CI build&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;When tagging a release, we first run a script which:

&lt;ul&gt;
&lt;li&gt;Iterates through all files under &lt;code&gt;changelogs/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copies each entry within each file into the global &lt;code&gt;CHANGELOG.md&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Deletes all individual entry files under &lt;code&gt;changelogs/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a few weeks of testing and tweaking the scripts which helped us automate this workflow, it turned out it worked really, really well for us.&lt;/p&gt;

&lt;p&gt;These scripts, however, lived directly in one repository, so the release notes process for our other projects remained a manual process. This prompted an evolution of the tooling such that it could be shared between projects.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HPzxwPxY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oungijjlv1ddquogz29e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HPzxwPxY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oungijjlv1ddquogz29e.png" alt="yaclt-logo-cropped"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Enter Yaclt, or: Yet Another ChangeLog Tool. Yaclt is the next evolution of this set of bash scripts; it's a proper command line tool, written in TypeScript, and highly configurable to meet the specific needs of a project, or even a specific developer.&lt;/p&gt;

&lt;p&gt;Each command is configurable with many options, and any option can also be specified in a configuration file. The configuration file supports yaml, JSON, or Javascript format; if you're using a Javascript configuration file, any option can be a function which returns the option value. This can be useful for things like computing the next release number from git history, or using a &lt;code&gt;package.json&lt;/code&gt; version as the release number.&lt;/p&gt;

&lt;p&gt;The tool also has some built-in features like parsing issue numbers from git branch names, given a format to match against, using the first line of your most recent commit message as the changelog message, opening generated changelogs in &lt;code&gt;$EDITOR&lt;/code&gt;, and more.&lt;/p&gt;

&lt;p&gt;Generating and validating changelog entries is super simple:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/584620060" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And so is collecting the entries into the global &lt;code&gt;CHANGELOG.md&lt;/code&gt; to prepare for a release:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/584620057" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;p&gt;This project is still a work in progress, and there are some great ideas for enhancements in the backlog, but it should be usable for your basic changelog workflow. Please open an issue if you run into any problems or pain points (or even better, submit a Pull Request)!&lt;/p&gt;

&lt;p&gt;Stars on the repository are greatly appreciated!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/mrjones2014"&gt;
        mrjones2014
      &lt;/a&gt; / &lt;a href="https://github.com/mrjones2014/yaclt"&gt;
        yaclt
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Yet Another Change Log Tool
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/mrjones2014/yaclt/actions/workflows/build.yml/badge.svg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rmuS6AUY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/mrjones2014/yaclt/actions/workflows/build.yml/badge.svg" alt="Build"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer" href="https://github.com/mrjones2014/yaclt/actions/workflows/publish.yml/badge.svg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tE06_kWU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/mrjones2014/yaclt/actions/workflows/publish.yml/badge.svg" alt="NPM Publish"&gt;&lt;/a&gt; &lt;a href="https://discord.gg/dv5x7tjqYk" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/877b7f2960953477146e231298d725f0c32a9409ca1b15dd58d68bec0fbd858f/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f3836383234363234353334333336373136393f6c6f676f3d646973636f7264267374796c653d666c6174" alt="Discord Server"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/mrjones2014/yaclt/raw/master/images/logo_color_on_transparent.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V2z74M7g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/mrjones2014/yaclt/raw/master/images/logo_color_on_transparent.png" alt="yaclt logo" height="225"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;What is &lt;code&gt;yaclt&lt;/code&gt;? It's &lt;strong&gt;Y&lt;/strong&gt;et &lt;strong&gt;A&lt;/strong&gt;nother &lt;strong&gt;C&lt;/strong&gt;hange&lt;strong&gt;L&lt;/strong&gt;og &lt;strong&gt;T&lt;/strong&gt;ool.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;yaclt&lt;/code&gt; is a file-based changelog generator. The idea is basically the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For each pull request/merge request, include a changelog file generated by this tool -- it gets stored in source control until the next release&lt;/li&gt;
&lt;li&gt;To prepare a release, use the &lt;code&gt;prepare-release&lt;/code&gt; command, which will
&lt;ul&gt;
&lt;li&gt;Gather all the individual changelog entries into a global &lt;code&gt;CHANGELOG.md&lt;/code&gt; (or whatever file you've specified)&lt;/li&gt;
&lt;li&gt;Delete all the individual changelog files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The changelog entry format and the global changelog file template are both &lt;a href="https://handlebarsjs.com" rel="nofollow"&gt;Handlebars&lt;/a&gt; templates, and you can use the extra helpers from &lt;a href="https://github.com/helpers/handlebars-helpers"&gt;&lt;code&gt;handlebars-helpers&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Help for each command can be found in &lt;a href="https://github.com/mrjones2014/yaclt./COMMANDS.md"&gt;COMMANDS.md&lt;/a&gt; or by running the command with the &lt;code&gt;--help&lt;/code&gt; argument.&lt;/p&gt;
&lt;h2&gt;
Dependencies&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;yaclt&lt;/code&gt; requires that &lt;code&gt;git&lt;/code&gt; exists on your &lt;code&gt;$PATH&lt;/code&gt; and that &lt;code&gt;node&lt;/code&gt; 12+ is installed.&lt;/p&gt;
&lt;h2&gt;
Configuration&lt;/h2&gt;
&lt;p&gt;All command line flags and arguments can be specified in…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/mrjones2014/yaclt"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>changelog</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Storing Dotfiles in a Git Repo</title>
      <dc:creator>Mat Jones</dc:creator>
      <pubDate>Sat, 22 May 2021 17:54:21 +0000</pubDate>
      <link>https://dev.to/matjones/storing-dotfiles-in-a-git-repo-342i</link>
      <guid>https://dev.to/matjones/storing-dotfiles-in-a-git-repo-342i</guid>
      <description>&lt;p&gt;Everyone has seen those &lt;code&gt;dotfiles&lt;/code&gt; repositories on GitHub. There's lots of different ways to manage them, but the method I use now requires no extra tooling (other than &lt;code&gt;git&lt;/code&gt; and a command line shell of your choice), no symbolic links to get files into the right locations, can be triggered from any directory on disk, and is easy to replicate on a new system.&lt;/p&gt;

&lt;p&gt;Basically what we're going to do is set up a git repository at &lt;code&gt;~/.cfg&lt;/code&gt; or &lt;code&gt;~/.dotfiles&lt;/code&gt; or any directory within your home directory of your choosing (although you probably &lt;em&gt;don't&lt;/em&gt; want to use &lt;code&gt;~/.config&lt;/code&gt; since that is the default value of &lt;a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html" rel="noopener noreferrer"&gt;$XDG_CONFIG_HOME&lt;/a&gt;), and a shell alias to help manage and control it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Repo
&lt;/h2&gt;

&lt;p&gt;If you're setting this up the first time, there's a few steps you'll need to take to set up. First, create the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init &lt;span class="nt"&gt;--bare&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.dotfiles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates a "bare" git repository at &lt;code&gt;~/.dotfiles&lt;/code&gt;. Now we'll set up an alias to interact with it from any directory on disk. Add the following alias to your &lt;code&gt;~/.bashrc&lt;/code&gt; or &lt;code&gt;~/.zshrc&lt;/code&gt; or &lt;code&gt;~/.config/fish/config.fish&lt;/code&gt; file, then &lt;code&gt;source&lt;/code&gt; the file:&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;# make sure the --git-dir is the same as the&lt;/span&gt;
&lt;span class="c"&gt;# directory where you created the repo above.&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"git --git-dir=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.dotfiles --work-tree=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;--work-tree=$HOME&lt;/code&gt; option sets the directory that the repository tracks to your home directory. Now, since there's probably more files in your home directory that you &lt;em&gt;don't&lt;/em&gt; want in the repo than files you &lt;em&gt;do&lt;/em&gt; want in the repo, you should configure the repo to not show untracked files by default. We can do that by setting a repository-local configuration option.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;config config &lt;span class="nt"&gt;--local&lt;/span&gt; status.showUntrackedFiles no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Tracking Files
&lt;/h2&gt;

&lt;p&gt;To track files in our new &lt;code&gt;~/.dotfiles&lt;/code&gt; repo, we just need to add them. From any directory on disk, you can run the following command to add your &lt;code&gt;~/.bashrc&lt;/code&gt; or &lt;code&gt;~/.zshrc&lt;/code&gt; or &lt;code&gt;~/.config/fish/config.fish&lt;/code&gt; file to your new dotfiles repo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;config add ~/.bashrc
config add ~/.zshrc
config add ~/.config/fish/config.fish

config commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add .bashrc/.zshrc/config.fish file"&lt;/span&gt;
config push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Installing on a New System
&lt;/h2&gt;

&lt;p&gt;Of course, the main point of doing this is to easily sync your config across new machines. We can easily do this with a small bash script to initialize the system's dotfiles from your git repository.&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;#!/usr/bin/env bash&lt;/span&gt;

git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; git@github.com:mrjones2014/dotfiles.git &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.dotfiles

&lt;span class="c"&gt;# define config alias locally since the dotfiles&lt;/span&gt;
&lt;span class="c"&gt;# aren't installed on the system yet&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;config &lt;span class="o"&gt;{&lt;/span&gt;
   git &lt;span class="nt"&gt;--git-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.dotfiles/ &lt;span class="nt"&gt;--work-tree&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# create a directory to backup existing dotfiles to&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .dotfiles-backup
config checkout
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Checked out dotfiles from git@github.com:mrjones2014/dotfiles.git"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Moving existing dotfiles to ~/.dotfiles-backup"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    config checkout 2&amp;gt;&amp;amp;1 | egrep &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="s2"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'print $1'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt; | xargs &lt;span class="nt"&gt;-I&lt;/span&gt;&lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; .dotfiles-backup/&lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# checkout dotfiles from repo&lt;/span&gt;
config checkout
config config status.showUntrackedFiles no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, installing your dotfiles on a new system is as simple as running:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://raw.githubusercontent.com/mrjones2014/dotfiles/master/scripts/config-init | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  That's Really It
&lt;/h2&gt;

&lt;p&gt;That's really all there is to it. Now you can easily add and track changes to dotfiles via your new &lt;code&gt;config&lt;/code&gt; shell alias.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;config add ~/.config/something/somefile
config commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"add somefile"&lt;/span&gt;
config push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Comment below with your dotfiles repo links! Feel free to browse mine for inspiration.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/mrjones2014" rel="noopener noreferrer"&gt;
        mrjones2014
      &lt;/a&gt; / &lt;a href="https://github.com/mrjones2014/dotfiles" rel="noopener noreferrer"&gt;
        dotfiles
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ❄️ My dotfiles for NixOS and macOS as a Nix flake. Neovim, Fish shell, Wezterm, etc.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Dotfiles&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;These are my NixOS and macOS dotfiles, packaged as a Nix flake, using &lt;a href="https://github.com/nix-community/home-manager" rel="noopener noreferrer"&gt;&lt;code&gt;home-manager&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For info on how to set up my Nix flake, see the &lt;a href="https://mjones.network/my-dotfiles.html" rel="nofollow noopener noreferrer"&gt;setup instructions&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why a Nix Flake&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Nix is an incredibly complex piece of software, but despite that, I believe that Nix is the only sensible way to manage software today. Using Nix, and particularly a Nix
flake, offers a few unique benefits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reproducibility&lt;/strong&gt;: Nix environments are described by text files (&lt;code&gt;*.nix&lt;/code&gt; files), and as long as you stay within the guard rails, the environment should be deterministic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: Packages can have access to their dependencies without those dependencies cluttering up the global environment — this also means different packages can depend on different versions of the same dependency without conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollbacks&lt;/strong&gt;: Totally screw up your environment by accident? Just roll back to a previous generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutability&lt;/strong&gt;…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/mrjones2014/dotfiles" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>bash</category>
      <category>vim</category>
      <category>dotfiles</category>
      <category>git</category>
    </item>
    <item>
      <title>How I Built My Personal Website</title>
      <dc:creator>Mat Jones</dc:creator>
      <pubDate>Fri, 22 Jan 2021 23:05:15 +0000</pubDate>
      <link>https://dev.to/matjones/how-i-built-my-personal-website-3k0n</link>
      <guid>https://dev.to/matjones/how-i-built-my-personal-website-3k0n</guid>
      <description>&lt;p&gt;I had a lot of fun building my personal site, because I used it as an opportunity to learn some new tech. Let's take a tour of what's in it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Core
&lt;/h1&gt;

&lt;p&gt;At it's core, the site is powered by &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; and &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt;. I used Next.js because the content is mostly static, which is a use-case Next excels at. Next enables Server Side Rendering (SSR) by default, but also supports Server Side Generation (SSG), which generates static HTML pages at build time. You can read more about the differences between SSR and SSG in &lt;a href="https://vercel.com/blog/nextjs-server-side-rendering-vs-static-generation"&gt;this great article by the Vercel team&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/vercel"&gt;
        vercel
      &lt;/a&gt; / &lt;a href="https://github.com/vercel/next.js"&gt;
        next.js
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      The React Framework
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Code Style
&lt;/h2&gt;

&lt;p&gt;In the project, I used React functional components with hooks &lt;em&gt;almost&lt;/em&gt; exclusively because I find I can write much cleaner and more concise components using the hooks API vs. traditional class-based components; the only exceptions being &lt;a href="https://nextjs.org/docs/advanced-features/custom-app"&gt;&lt;code&gt;_app.tsx&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://nextjs.org/docs/advanced-features/custom-document"&gt;&lt;code&gt;_document.tsx&lt;/code&gt;&lt;/a&gt;, which extend base classes from the Next framework. A &lt;code&gt;_document.tsx&lt;/code&gt; file allows you to customize the base HTML template that the React app is injected into, while &lt;code&gt;_app.tsx&lt;/code&gt; allows you to use shared layouts, add an &lt;a href="https://reactjs.org/docs/error-boundaries.html"&gt;Error Boundary&lt;/a&gt;, import global stylesheets, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Customization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Markdown Copy Content
&lt;/h3&gt;

&lt;p&gt;I've also added &lt;a href="https://www.npmjs.com/package/@mdx-js/loader"&gt;&lt;code&gt;@mdx-js/loader&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/@next/mdx"&gt;&lt;code&gt;@next/mdx&lt;/code&gt;&lt;/a&gt; to my build configuration, which allows me to import markdown (*.md) files as React components. Using this technique allows me to keep some of my copy text (such as the "about" page) in markdown rather than having to manually write out the JSX for it, which is just simpler and easier to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working with SVGs
&lt;/h3&gt;

&lt;p&gt;I'm using &lt;a href="https://github.com/gregberge/svgr"&gt;SVGR&lt;/a&gt; to transform my SVG files into React components, rather than loading them via &lt;code&gt;&amp;lt;img src="/my-image.svg"/&amp;gt;&lt;/code&gt;. This means my SVGs will be loaded with the markup, rather than being loaded asynchronously, which can help eliminate &lt;a href="https://web.dev/cls/"&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; issues. It's as simple as customizing your &lt;code&gt;next.config.js&lt;/code&gt; file to use the &lt;code&gt;@svgr/webpack&lt;/code&gt; loader for &lt;code&gt;*.svg&lt;/code&gt; file types, then importing them in your components, a la:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SvgAvatar&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/avatar.svg&lt;/span&gt;&lt;span class="dl"&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;MyComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&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="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SvgAvatar&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/MyComponent&lt;/span&gt;&lt;span class="err"&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;
  
  
  DEV Blog
&lt;/h3&gt;

&lt;p&gt;I previously wrote another article about how we can use the DEV API to load your articles in order to embed them in your own website.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/matjones" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LYieCLc---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--8lsrKd-0--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/561062/f3d74754-405b-4425-b408-2a4c06adefcb.png" alt="matjones image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/matjones/how-to-embed-your-dev-to-blog-in-your-personal-website-4l81" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to Embed Your Dev.to Blog in Your Personal Website&lt;/h2&gt;
      &lt;h3&gt;Mat Jones ・ Jan 18 ・ 5 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;I combined this technique with Next's &lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation"&gt;&lt;code&gt;getStaticProps&lt;/code&gt;&lt;/a&gt; method to load all my DEV articles at build time, and statically generate the blog pages for my site. To keep the pages up-to-date, I configured the &lt;a href="https://github.com/features/actions"&gt;GitHub Action&lt;/a&gt; that builds and deploys my site to run periodically. I &lt;a href="https://github.com/mrjones2014/mrjones2014.github.io/blob/master/.github/workflows/integrate.yml"&gt;configured it to run every 30 minutes&lt;/a&gt;, however in practice it seems like for some reason it actually runs closer to once every hour. If your CI/CD pipeline is actually time-sensitive, I recommend not using GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;h2&gt;
  
  
  UI Framework
&lt;/h2&gt;

&lt;p&gt;On a recommendation from a friend, I decided to learn &lt;a href="https://blueprintjs.com/"&gt;Blueprint.js&lt;/a&gt; and use it to build my website. After building lots of components using it, tweaking it, and styling it, I think it has become my new favorite React UI framework.&lt;/p&gt;

&lt;p&gt;Right out of the box, everything looks amazing, cohesive, and enterprise-quality. Their component library is pretty extensive, and includes just about everything you'd want in your base toolkit, from simple stuff like &lt;a href="https://blueprintjs.com/docs/#core/components/navbar"&gt;navbars&lt;/a&gt; and &lt;a href="https://blueprintjs.com/docs/#core/components/button"&gt;buttons&lt;/a&gt;, to layout things like &lt;a href="https://blueprintjs.com/docs/#core/components/card"&gt;cards&lt;/a&gt;, to more complex components like &lt;a href="https://blueprintjs.com/docs/#core/components/menu"&gt;interactive menus&lt;/a&gt;, &lt;a href="https://blueprintjs.com/docs/#core/components/progress-bar"&gt;progress bars&lt;/a&gt; and &lt;a href="https://blueprintjs.com/docs/#core/components/drawer"&gt;drawers&lt;/a&gt;. It's pretty easy to customize and compose the built-in components to make more complex designs. My only complaint is that there are a select few places I had to use the &lt;a href="https://uxengineer.com/css-specificity-avoid-important-css/#why-avoid-!important"&gt;dreaded &lt;code&gt;!important&lt;/code&gt; CSS directive&lt;/a&gt; to override some specific styles from Blueprint's stylesheets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stylesheets
&lt;/h2&gt;

&lt;p&gt;My go-to language for stylesheets has been &lt;a href="https://sass-lang.com/"&gt;SCSS&lt;/a&gt; for a long time, because, well, let's face it, &lt;a href="https://hackernoon.com/why-you-shouldnt-write-vanilla-v92333xn"&gt;writing vanilla CSS just sucks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the greatest features of CSS preprocessors like Sass is the &lt;a href="https://sass-lang.com/documentation/style-rules#nesting"&gt;ability to write nested styles&lt;/a&gt;; if your markup can be nested, then why can't your stylesheets?!&lt;/p&gt;

&lt;p&gt;You can also easily re-use bits of common styles via &lt;a href="https://sass-lang.com/documentation/at-rules/mixin"&gt;mixins&lt;/a&gt;, and even run compile-time &lt;a href="https://sass-lang.com/documentation/operators"&gt;logical operations&lt;/a&gt; to further customize the generated stylesheets. It's incredibly powerful and extensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;My site is hosted statically on &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;, configured to deploy from the &lt;code&gt;gh-pages&lt;/code&gt; branch of the repository. Deployment happens automatically on push, periodically, and can be manually triggered via a &lt;a href="https://github.com/mrjones2014/mrjones2014.github.io/blob/master/.github/workflows/integrate.yml"&gt;GitHub Actions workflow&lt;/a&gt;. The date of the most recent build is injected into the app at compile time via an environment variable in &lt;code&gt;next.config.js&lt;/code&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Embed Your Dev.to Blog in Your Personal Website</title>
      <dc:creator>Mat Jones</dc:creator>
      <pubDate>Mon, 18 Jan 2021 21:57:36 +0000</pubDate>
      <link>https://dev.to/matjones/how-to-embed-your-dev-to-blog-in-your-personal-website-4l81</link>
      <guid>https://dev.to/matjones/how-to-embed-your-dev-to-blog-in-your-personal-website-4l81</guid>
      <description>&lt;p&gt;I recently switched my blog over from Medium to DEV, one of the primary reasons being I wanted an easy way to show my full blog on my personal website, as well as on the platform I'm using.&lt;/p&gt;

&lt;p&gt;In Medium's desperate quest for monetization, they've made it extremely difficult to embed your blog on your own website. There are several projects out there on GitHub that attempt to help do this, but none of them seem to work very well, and they're all somewhat lacking in the styles department.&lt;/p&gt;

&lt;p&gt;DEV has an open API, making it much easier to embed your blog in your own website, with full custom markup and styles, so you can easily make it look exactly how you want. In this article, we'll take a look at just how easy this is to get set up.&lt;/p&gt;

&lt;p&gt;We're going to be using TypeScript, but if you're not familiar with it or just aren't using it, you should still be able to follow along, just excluding the TypeScript interfaces.&lt;/p&gt;

&lt;h1&gt;
  
  
  API
&lt;/h1&gt;

&lt;p&gt;I'm going to be using &lt;a href="https://github.com/visionmedia/superagent" rel="noopener noreferrer"&gt;Superagent&lt;/a&gt; as my HTTP client, because I like the functional and declarative API, but you can easily swap this out for something else like Axios, or the browser Fetch API. The full API documentation for DEV can be found &lt;a href="https://docs.dev.to/api/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's start by setting up some interfaces to type our responses with (if you're using vanilla JavaScript you can skip this step).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dev-to-article-meta.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;DevToArticleMeta&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type_of&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;readable_publish_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;comments_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;public_reactions_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;collection_id&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;published_timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;positive_reactions_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cover_image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;social_image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;tag_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&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;&lt;code&gt;dev-to-article.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DevToArticleMeta&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;interfaces/dev-to-article-meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;DevToArticle&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;DevToArticleMeta&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;body_html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;body_markdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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 let's set up a service to handle fetching our data. We'll need two methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;async fetchArticles(): Promise&amp;lt;Array&amp;lt;DevToArticleMeta&amp;gt;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;async getArticle(slug: string): Promise&amp;lt;DevToArticle&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;dev-to-service.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DevToArticle&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;interfaces/dev-to-article&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DevToArticleMeta&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;interfaces/dev-to-article-meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;superagent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;superagent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// setup API endpoints and queries&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DEV_TO_USERNAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;matjones&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// swap this for your username&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ARTICLES_API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to/api/articles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// helper method to type responses&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;T&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;typeof&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;T&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;fetchArticles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;// GET the endpoint&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;superagent&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;ARTICLES_API&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="c1"&gt;// and add the username query parameter&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DEV_TO_USERNAME&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;getArticle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;// build the API endpoint URL, `slug` is the `slug`&lt;/span&gt;
  &lt;span class="c1"&gt;//property of one of your articles,&lt;/span&gt;
  &lt;span class="c1"&gt;// e.g. "protecting-your-privacy-online-3bmc"&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ARTICLES_API&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="nx"&gt;DEV_TO_USERNAME&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="nx"&gt;slug&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;superagent&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;endpoint&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;parseResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DevToArticle&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DevToService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fetchArticles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getArticle&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;And that's pretty much it for our API layer. Nice and simple. 😎&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fetchArticles()&lt;/code&gt; function will fetch all of your published articles. If you want to implement pagination or a maximum number of results, you can add the &lt;a href="https://docs.dev.to/api/#operation/getArticles"&gt;&lt;code&gt;page&lt;/code&gt; and &lt;code&gt;per_page&lt;/code&gt; query parameters&lt;/a&gt; via the &lt;code&gt;.query()&lt;/code&gt; method chained off of &lt;code&gt;superagent.get()&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Integrating Into a React App
&lt;/h1&gt;

&lt;p&gt;While this is technically all you need to get the data needed to render your articles, we can make it a little easier to work with in a React app by adding some &lt;a href="https://reactjs.org/docs/hooks-custom.html" rel="noopener noreferrer"&gt;custom React hooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;STOP: If you're not familiar with React hooks, &lt;a href="https://reactjs.org/docs/hooks-intro.html" rel="noopener noreferrer"&gt;go read up on hooks first&lt;/a&gt;, then come back. I'll wait.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'll make a really simple hook for each of our service methods. Since &lt;code&gt;useEffect&lt;/code&gt; callbacks can't be &lt;code&gt;async&lt;/code&gt; themselves, we're going to be using an &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/IIFE" rel="noopener noreferrer"&gt;IIFE&lt;/a&gt;, but note that you could also use the trusty old &lt;code&gt;Promise.then()&lt;/code&gt; syntax as well.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;use-dev-to-articles.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DevToArticleMeta&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;interfaces/dev-to-article-meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DevToService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utils/dev-to-service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
* Fetch all of my published dev.to articles
* @param onError a callback which is invoked if the request fails
*/&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useDevToArticles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onError&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="k"&gt;void&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&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="kc"&gt;true&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;articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setArticles&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DevToArticleMeta&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&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="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setArticles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;DevToService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchArticles&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;?.();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;span class="nx"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// return the array of articles, and the loading indicator&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;articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&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;&lt;code&gt;use-dev-to-article.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DevToArticle&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;interfaces/dev-to-article&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DevToService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utils/dev-to-service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
* Get a specific article given the article's slug.
* @param slug the slug of the article to retrieve.
* @param onError a callback which is invoked if the request fails
*/&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useDevToArticle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onError&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="k"&gt;void&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&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="kc"&gt;true&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;article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setArticle&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DevToArticle&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;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="c1"&gt;// this bit may not be necessary for you; I needed&lt;/span&gt;
    &lt;span class="c1"&gt;// it because I'm using Next.js server side rendering&lt;/span&gt;
    &lt;span class="c1"&gt;// so slug is `undefined` on the initial render&lt;/span&gt;
    &lt;span class="c1"&gt;// since I'm getting it from a route parameter&lt;/span&gt;
    &lt;span class="c1"&gt;// e.g. /blog/:slug&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;slug&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&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="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setArticle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;DevToService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getArticle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;?.();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;span class="nx"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="c1"&gt;// return the article, and the loading indicator&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;article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&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;
  
  
  Rendering an Article's Body
&lt;/h2&gt;

&lt;p&gt;There are two ways you can render an article's body; using the &lt;code&gt;body_html&lt;/code&gt; property or the &lt;code&gt;body_markdown&lt;/code&gt; property. If your article does not contain any HTML elements (you're only using Markdown) and does not contain any DEV &lt;a href="https://docs.dev.to/frontend/liquid-tags/"&gt;Liquid tags&lt;/a&gt;, you can use the &lt;code&gt;body_markdown&lt;/code&gt; property and use something like &lt;a href="https://github.com/remarkjs/react-markdown" rel="noopener noreferrer"&gt;&lt;code&gt;react-markdown&lt;/code&gt;&lt;/a&gt; to render it.&lt;/p&gt;

&lt;p&gt;However, if you're using Liquid tags, or you have HTML elements in your markdown (for example, I often use the &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; element to add captions to images and code snippets), you'll need to render the &lt;code&gt;body_html&lt;/code&gt; property. In React, this can be done like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body_html&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; syntax; it is &lt;em&gt;intentionally&lt;/em&gt; ugly to look at and difficult to type quickly, because doing this could potentially open your app to cross-site-scripting vulnerabilities. In general, you should only use &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; with trusted inputs (e.g. inputs that you control, not unknown end users).&lt;/p&gt;

&lt;h1&gt;
  
  
  Results
&lt;/h1&gt;

&lt;p&gt;Using this approach, I was able to easily embed my DEV blog into my personal website. Clicking the comment or thumbs up icon opens the article on dev.to so that you can add reactions or comments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmmx1dqqz4ow3k9ayj7av.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmmx1dqqz4ow3k9ayj7av.png" alt="Blog index page on my personal site"&gt;&lt;/a&gt;&lt;/p&gt;
The /blog page on my personal website.



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ooiwuhddje71w0jpoxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ooiwuhddje71w0jpoxl.png" alt="Blog article page on my personal site"&gt;&lt;/a&gt;&lt;/p&gt;
A specific blog article on my personal website.



</description>
    </item>
    <item>
      <title>The Ultimate Work From Home Programming Setup</title>
      <dc:creator>Mat Jones</dc:creator>
      <pubDate>Sat, 16 Jan 2021 14:59:25 +0000</pubDate>
      <link>https://dev.to/matjones/the-ultimate-work-from-home-programming-setup-3cp4</link>
      <guid>https://dev.to/matjones/the-ultimate-work-from-home-programming-setup-3cp4</guid>
      <description>&lt;p&gt;When COVID-19 first broke out, it forced a massive shift in the workplace; the only way to survive as a business was to have employees work from home. And it seems like work from home policies are here to stay, at least in the tech industry. The company I work for only had an “as needed” work from home policy before the pandemic, but now we will have an option to work from home full time, even after the office reopens, an option which I think many will take advantage of.&lt;/p&gt;

&lt;p&gt;So, since we’re going to be working from home for the foreseeable future, I decided to upgrade my home office setup. I’ll take you through each piece of hardware, and why I like it for programming.&lt;/p&gt;

&lt;h1&gt;
  
  
  Monitors
&lt;/h1&gt;

&lt;p&gt;Monitors are arguably the most important pieces of hardware in a programmer’s home office. As a software engineer, I usually need to have a lot of applications open at once, so maximizing screen real estate was my main objective when selecting monitors.&lt;/p&gt;

&lt;p&gt;My main monitor is a &lt;a href="https://www.samsung.com/us/computing/monitors/gaming/49--chg90-qled-gaming-monitor-lc49hg90dmnxza/"&gt;49" Samsung CHG90 QLED 1080P Curved Super Ultrawide 32:9 Gaming Monitor&lt;/a&gt; on it’s stock stand. It might seem excessive, but it’s a truly incredible experience for programming. Typically, I use the right two-thirds of the monitor for my IDE, and I can fit 2–3 open code editors side by side, and the left one-third is for my web browser, running the web app I’m working on. The one caveat is that native window management doesn’t handle this very well, so I use a third-party window management software called &lt;a href="https://mizage.com/divvy/"&gt;Divvy&lt;/a&gt;, which allows you to use keyboard shortcuts to snap windows to user defined areas of the screen.&lt;/p&gt;

&lt;p&gt;My secondary monitor is a &lt;a href="https://www.viewsonic.com/us/products/shop/monitors/vx2478-smhd.html"&gt;24" ViewSonic 1440P VX2478 series&lt;/a&gt;, mounted vertically (portrait mode) on a &lt;a href="https://www.vivo-us.com/collections/monitor-mounts/products/stand-v001"&gt;VIVO Single Monitor Desk Mount&lt;/a&gt;. I have this monitor mounted on the left side of my desk, and use it almost exclusively for &lt;a href="https://slack.com/"&gt;Slack&lt;/a&gt;. It’s great to be able to see a lot of the previous messages in portrait mode, and it keeps slack out of my way on my main monitors.&lt;/p&gt;

&lt;p&gt;My final monitor is a &lt;a href="https://www.sceptre.com/Monitors/Curve-Monitor/C355W-3440UN-35-Curved-Monitor-product1176category12category80.html"&gt;35" Sceptre C355W-3440UN 1440P Curved Ultrawide&lt;/a&gt; mounted above my main monitor on a &lt;a href="https://www.vivo-us.com/collections/monitor-mounts/products/stand-v001t"&gt;VIVO Single Monitor Extra Tall Desk Mount&lt;/a&gt;. It’s a great, affordable ultrawide option that competes with much more expensive models from other manufacturers like LG or Dell. I use the left half of this monitor for &lt;a href="https://clickup.com/"&gt;ClickUp&lt;/a&gt;, the project management and agile board software we use at work, and the right half for [Spotify](&lt;a href="https://www.spotify.com/us/%5C"&gt;https://www.spotify.com/us/\&lt;/a&gt;) and any other miscellaneous software I may need to use throughout the course of the day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L7Ix3QwP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f30z284awiko6c2zbpmj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L7Ix3QwP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f30z284awiko6c2zbpmj.png" alt="My monitor orientation setup"&gt;&lt;/a&gt;&lt;/p&gt;
My monitor orientation setup.



&lt;p&gt;With curved ultrawide monitors, you typically want to sit farther away from them than a typical flat 16:9 monitor in order to get a wider field of view, so I have all of these monitors sitting on an &lt;a href="https://www.ikea.com/us/en/p/linnmon-adils-corner-table-black-brown-silver-color-s99932173/"&gt;IKEA LINNMON / ADILS corner desk&lt;/a&gt;, which gives me a lot of depth space to push the monitors back into the corner.&lt;/p&gt;

&lt;h1&gt;
  
  
  Peripherals
&lt;/h1&gt;

&lt;p&gt;Let’s start with the obvious; a great mechanical keyboard is a necessity for a programmer. Right now, I’m using a &lt;a href="https://www.durgod.com/page9?_l=en&amp;amp;product_id=47"&gt;DURGOD Taurus K320 Mechanical Keyboard&lt;/a&gt; with Cherry MX Clear switches, and so far it’s the best, most satisfying mechanical keyboard I’ve ever used. Don’t be turned off by the &lt;a href="https://www.amazon.com/DURGOD-Mechanical-Keyboard-Interface-Anti-Ghosting/dp/B078HFTTYK"&gt;relatively low price point&lt;/a&gt;; it has an &lt;strong&gt;excellent&lt;/strong&gt; build quality, with virtually no key wobble, stabilizers for the long keys (enter, shift, backspace, etc.), a super solid frame with zero flex, and extremely high quality plastics for all the parts. I really like Cherry MX Clear switches with it, because it provides an extremely stiff keypress (though this is just based on preference) while still not being obnoxiously loud, like Cherry MX Blues.&lt;/p&gt;

&lt;p&gt;For my mouse, I use a &lt;a href="https://www.cherrymx.de/en/mx-original/mx-clear.html"&gt;Logitech G502&lt;/a&gt;. It has removable weights so you can configure it to your preferred weight (I prefer a heavier mouse), but the main attraction here is really the scroll wheel. The G502 has a button that can toggle the heavy, weighted scroll wheel between behaving like any other scroll wheel and being completely free spinning (check out the video below to see what I mean). It’s amazing for scrolling through huge code files that are hundreds of lines long. I’ve also considered picking up a &lt;a href="https://www.logitech.com/en-us/products/mice/mx-master-3.910-005620.html"&gt;Logitech MX Master 3&lt;/a&gt;, which has the same scroll wheel feature, but also has a thumb wheel for horizontal scrolling, which seems interesting.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/DljQV3pE1lc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I also recommend getting an oversized mouse+keyboard mat, like the &lt;a href="https://www.logitechg.com/en-us/products/gaming-mice/g502-hero-gaming-mouse.910-005469.html"&gt;Logitech G840 XL&lt;/a&gt;. It may seem excessive, but it’s really nice to have a soft surface to put both your mouse and keyboard on, especially since it helps to prevent your keyboard from sliding like it might on a harder surface.&lt;/p&gt;

&lt;p&gt;If you’re a programmer, software engineer, or tech industry professional of any kind, chances are you might be working from home for a long time, or indefinitely. If that’s the case, it might be time to upgrade your home office.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Code Comments Are Stupid</title>
      <dc:creator>Mat Jones</dc:creator>
      <pubDate>Sat, 16 Jan 2021 14:49:49 +0000</pubDate>
      <link>https://dev.to/matjones/code-comments-are-stupid-m5n</link>
      <guid>https://dev.to/matjones/code-comments-are-stupid-m5n</guid>
      <description>&lt;p&gt;Code comments are lame.&lt;/p&gt;

&lt;p&gt;Code comments once served a critical purpose; adding context or explanation to a snippet of code. But this was back when programming languages were mostly esoteric, and the barrier to entry for writing code was extremely high.&lt;/p&gt;

&lt;p&gt;Today, though, we have extremely &lt;a href="https://stackoverflow.com/a/638929"&gt;expressive&lt;/a&gt; programming languages, like C# (especially since the addition of &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/"&gt;LINQ&lt;/a&gt;), Python, Kotlin, etc. that make code comments virtually obsolete. With a highly expressive language, nearly all comments become unnecessary, because it’s easy to write &lt;a href="https://en.wikipedia.org/wiki/Self-documenting_code"&gt;self-documenting code&lt;/a&gt;; the code is easy enough to understand at a glance that you don’t need comments anymore.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You should always write your code as if comments didn’t exist.” — Jeff Atwood (&lt;a href="https://blog.codinghorror.com/coding-without-comments/"&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ll be writing most of my code examples in &lt;a href="https://kotlinlang.org/"&gt;Kotlin&lt;/a&gt; because it’s one of my favorite languages, and I haven’t gotten to use it recently, but all these examples are applicable to any sufficiently expressive language.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementation Comments
&lt;/h1&gt;

&lt;p&gt;Most of the time, comments in the implementation of a bit of code can be made unnecessary just by naming things better, and keeping your functions and methods small and focused on a single task. Let’s consider a simple example.&lt;/p&gt;

&lt;p&gt;We’re working on a web application, and we want to implement a method to validate that a password meets certain required criteria. For this example, let’s say our criteria are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Must contain at least one uppercase letter and at least one lowercase letter&lt;/li&gt;
&lt;li&gt;  Must contain one number&lt;/li&gt;
&lt;li&gt;  Must not include whitespace&lt;/li&gt;
&lt;li&gt;  Must contain one of the following special characters: ! # $ % ^ &amp;amp; *&lt;/li&gt;
&lt;li&gt;  Must be between 8 and 20 digits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; do it in very few lines of code with a complex &lt;a href="https://en.wikipedia.org/wiki/Regular_expression"&gt;regular expression&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Validate that the given password matches our password criteria.
 * @param password the password to check against our criteria
 * @return         true if the password matches all criteria, false otherwise
 */&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Pattern matches that:&lt;/span&gt;
    &lt;span class="c1"&gt;// - Must contain at least one uppercase letter and at least one lowercase letter&lt;/span&gt;
    &lt;span class="c1"&gt;// - Must contain one number&lt;/span&gt;
    &lt;span class="c1"&gt;// - Must not include whitespace&lt;/span&gt;
    &lt;span class="c1"&gt;// - Must contain one of the following special characters: ! # $ % ^ &amp;amp; * &lt;/span&gt;
    &lt;span class="c1"&gt;// - Must be between 8 and 20 digits&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;pattern&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!#$%^&amp;amp;*])[\S]{8,20}$"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toRegex&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Passw0rd!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"P assw0rd!"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// false; contains whitespace&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Password!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; no numeric digits&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Passw0rd"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; no special characters&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"password!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; no uppercase letter&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PASSW0RD!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false, no lowercase letter&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pswrd!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; too short&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UltraL0ngSecurePassw0rd!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; too long&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
A great way to make enemies is to make sure nobody else can understand your code.





&lt;p&gt;Without those comments inside of the &lt;code&gt;isPasswordValid&lt;/code&gt; function, it’s really cryptic and almost impossible to tell what it’s doing without taking a ton of time to figure out that really complicated regular expression.&lt;/p&gt;

&lt;p&gt;By splitting each password criterion into a distinct unit of work, the code and the regular expressions used in the code both become &lt;strong&gt;a lot&lt;/strong&gt; clearer and easier for a reader to &lt;a href="https://en.wikipedia.org/wiki/Grok"&gt;grok&lt;/a&gt; with just a glance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;containsUppercaseLetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""(?=.*[A-Z]).*"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toRegex&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;containsLowercaseLetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""(?=.*[a-z]).*"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toRegex&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;containsNumericDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""(?=.*[0-9]).*"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toRegex&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;doesNotContainWhitespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&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="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""(?=.*[\s]).*"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toRegex&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;containsSpecialCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"""(?=.*[!#$%^&amp;amp;*\.]).*"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toRegex&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;lengthIsInRangeInclusive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Validate that the given password matches our password criteria.
 * @param password the password to check against our criteria
 * @return         true if the password matches all criteria, false otherwise
 */&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;lengthIsInRangeInclusive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="nf"&gt;containsUppercaseLetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="nf"&gt;containsLowercaseLetter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="nf"&gt;containsNumericDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="nf"&gt;doesNotContainWhitespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
            &lt;span class="nf"&gt;containsSpecialCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Passw0rd!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"P assw0rd!"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// false; contains whitespace&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Password!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; no numeric digits&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Passw0rd"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; no special characters&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"password!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; no uppercase letter&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PASSW0RD!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false, no lowercase letter&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pswrd!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; too short&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UltraL0ngSecurePassw0rd!"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// false; too long&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
A few more lines of code can go a long way for clarity.





&lt;p&gt;The code is only a few more lines long, but is &lt;strong&gt;so much clearer&lt;/strong&gt; at a glance that we don’t even need that huge comment block anymore. We’ve converted a comment telling us what the code is doing into code that tells us itself what it’s doing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Redundant Comments
&lt;/h1&gt;

&lt;p&gt;This is the most useless (and annoying) type of code comment. These are the type of comments where you can just remove the comment without even changing the code, and it’s still just as clear what the code is doing.&lt;/p&gt;

&lt;p&gt;Let’s say we’re working on a command line tool grab some data from an API and write it to a file. We’ll need to ask the user for a file path to write the data to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;callApi&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;ApiResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// implementation omitted&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;writeJsonToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// implementation omitted&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Call the API and get the results&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;apiResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;callApi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Get the desired file path to save the results to&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter file path to save results:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// make sure it's a JSON file&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".json"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${filePath}.json"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// write the data to the json file&lt;/span&gt;
&lt;span class="nf"&gt;writeJsonToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Seems good… or not?





&lt;p&gt;There’s a lot cluttering up this code, though. It may seem a bit complicated, but that’s just because of the comments. In situations like this, removing the comments can actually make the code &lt;em&gt;less&lt;/em&gt; intimidating to a reader.&lt;/p&gt;

&lt;p&gt;When you see a function defined as &lt;code&gt;writeJsonToFile(json: String, filePath: String)&lt;/code&gt;, with no comments or explanation, what do you think it does? You can probably immediately understand that “it writes some JSON data to a file”. We can remove every single comment, and the code is still just as clear, readable, and understandable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;callApi&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;ApiResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// implementation omitted&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;writeJsonToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// implementation omitted&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;apiResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;callApi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter file path to save results: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readLine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&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="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".json"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${filePath}.json"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;writeJsonToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Its still just as easy to understand this code without the comments.





&lt;p&gt;We didn’t change any of the code here, we just removed all the comments. Yet, it still seems less intimidating on your first read, right? Appropriately named variables and functions make all the comments completely redundant, so the comments were just clutter.&lt;/p&gt;

&lt;h1&gt;
  
  
  Exceptions to the Rule
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Documentation Comments
&lt;/h2&gt;

&lt;p&gt;The main exception to this is tooling-specific documentation comments, such as &lt;a href="https://jsdoc.app/"&gt;JSDoc&lt;/a&gt; and &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/"&gt;C# XML Documentation Comments&lt;/a&gt;. These types of comments provide popup API documentation via IntelliSense (or another autocomplete mechanism) in most IDEs and editors, like &lt;a href="https://code.visualstudio.com/"&gt;VS Code&lt;/a&gt;, &lt;a href="https://www.jetbrains.com/rider/"&gt;Rider&lt;/a&gt;, and others.&lt;/p&gt;

&lt;p&gt;Documentation comments also usually hook into documentation generator tools, like &lt;a href="https://dotnet.github.io/docfx/"&gt;DocFX&lt;/a&gt; or &lt;a href="https://github.com/EWSoftware/SHFB"&gt;Sandcastle&lt;/a&gt;, which can automatically generate HTML documentation web pages from your documentation comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clarification Comments
&lt;/h2&gt;

&lt;p&gt;Sometimes, there is an obvious solution that seems simpler or better, but there is a specific reason the developer chose not to use that solution. I’ve found this to be &lt;strong&gt;extremely&lt;/strong&gt; common in JavaScript. Let’s take a look at an example. We’ll use TypeScript to help make things a bit clearer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;isFinite&lt;/span&gt; &lt;span class="o"&gt;=&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="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;// don't use the global window.isFinite(value)&lt;/span&gt;
  &lt;span class="c1"&gt;// because it returns true for null/undefined&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;isFinite&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NumberUtils&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;isFinite&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;
Finally, an appropriate place for a comment.





&lt;p&gt;In this example, the comment is warranted, because it prevents future developers from introducing bugs without realizing it by changing &lt;code&gt;return Number.isFinite(value);&lt;/code&gt; to just &lt;code&gt;return isFinite(value);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Another common example is when you need to prevent bugs caused by Internet Explorer having different implementations of built-in functions than every other browser, because reasons. Let’s look at another example in TypeScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;// Don't directly return the value of `set.add`&lt;/span&gt;
  &lt;span class="c1"&gt;// because it's not chainable in IE 11&lt;/span&gt;
  &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&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="kd"&gt;set&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SetUtils&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;addEntry&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;
Internet Explorer sucks.





&lt;p&gt;Once again, in this example, the comment prevents a future developer from unknowingly introducing Internet Explorer-specific bugs by changing this implementation to just &lt;code&gt;return set.add(value);&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;Modern programming languages have become so expressive that code comments are virtually obsolete, except in specific circumstances. The time has come to replace code comments with highly expressive, self-documenting code.&lt;/p&gt;

</description>
      <category>comments</category>
      <category>cleancode</category>
      <category>programming</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Strongly Typed Error Handling in TypeScript</title>
      <dc:creator>Mat Jones</dc:creator>
      <pubDate>Sat, 16 Jan 2021 14:42:46 +0000</pubDate>
      <link>https://dev.to/matjones/strongly-typed-error-handling-in-typescript-2m49</link>
      <guid>https://dev.to/matjones/strongly-typed-error-handling-in-typescript-2m49</guid>
      <description>&lt;p&gt;TypeScript is a great language. TypeScript takes JavaScript and makes it &lt;em&gt;actually good.&lt;/em&gt; If there’s one glaring weakness, it’s the &lt;a href="https://github.com/Microsoft/TypeScript/issues/8677"&gt;inability to use strongly typed catch blocks&lt;/a&gt;. However, this is mostly due to a design flaw in the JavaScript language; in JavaScript, you can &lt;code&gt;throw&lt;/code&gt; &lt;em&gt;anything&lt;/em&gt;, not just &lt;code&gt;Error&lt;/code&gt; types.&lt;/p&gt;

&lt;h1&gt;
  
  
  Justification
&lt;/h1&gt;

&lt;p&gt;Consider the following, completely valid TypeScript code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "number"&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 7&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
You can quite literally throw anything.





&lt;p&gt;It’s easy to see why this can be a problem.  One use-case where this is less than ideal is consuming a web API from TypeScript. It’s quite common for non-success HTTP status codes (500 Internal Server Error, 404 Not Found, etc.) to be thrown as an error by API consumer code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Usage
&lt;/h1&gt;

&lt;p&gt;All models are &lt;code&gt;Immutable.js&lt;/code&gt; &lt;code&gt;Record&lt;/code&gt; classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
Let's say the API returns a 500 error, with an error
result JSON object:
-- Status: 500
-- Data:
{
  "errors": [
    {
      "key": "UserAlreadyExists",
      "message": "User with specified email already exists.",
      "type": "Validation"
    }
  ]
  "resultObject": undefined
}
*/&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUserApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ServiceFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/users/&lt;/span&gt;&lt;span class="dl"&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;createUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&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;UserRecord&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserRecord&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;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;try&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;createUserApi&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="k"&gt;return&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;resultObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// if the thing that was thrown is a ResultRecord, show a toast message for each error&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;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;ResultRecord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ResultErrorRecord&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;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// otherwise, it could be anything, so just show a generic error message&lt;/span&gt;
    &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;There was an issue creating the user.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// maybe we need to turn off a loading indicator here&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 can see in this example that handling errors natively in TypeScript is… quite sloppy. The &lt;a href="https://en.wikipedia.org/wiki/Monad_%28functional_programming%29#An_example:_Maybe"&gt;“maybe monad”&lt;/a&gt; common pattern to more generically handle errors and control flow. Basically, what we want to do is create an abstraction that can strongly type thrown errors to a specified type that you know is likely to be thrown. In our case, we want to be able to handle errors from a strongly typed &lt;code&gt;ResultRecord&lt;/code&gt; with &lt;code&gt;ResultErrorRecord&lt;/code&gt;s inside it.&lt;/p&gt;

&lt;p&gt;What if we could take the example above, and represent the same logic but with less code and strong typing in the catch block? In the following example, one of &lt;code&gt;result&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt; will be non-null, but not both.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
Let's say the API returns the same response as before.
result JSON object:
-- Status: 500
-- Data:
{
  "errors": [
    {
      "key": "UserAlreadyExists",
      "message": "User with specified email already exists.",
      "type": "Validation"
    }
  ]
  "resultObject": undefined
}
*/&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createUserApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ServiceFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserRecord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/users/&lt;/span&gt;&lt;span class="dl"&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;createUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&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;UserRecord&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserRecord&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;createUserApi&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="k"&gt;return&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;resultObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&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;ResultRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserRecord&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;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&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;// if result is not null, it will have errors; show toast errors&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;errors&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ResultErrorRecord&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;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// otherwise, unknown error; show a generic toast message&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;There was an issue creating the user.&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="k"&gt;finally&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;// maybe we need to turn off a loading indicator here&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;getAwaiter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Automatic typecasting? Nice 😎





&lt;p&gt;This pattern gives us a more functional approach to error handling, gives us &lt;strong&gt;strongly typed&lt;/strong&gt; errors, and works really, really nicely when used in combination with &lt;a href="https://reactjs.org/docs/hooks-intro.html"&gt;React hooks&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoginForm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionComponent&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="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="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;createUserApi&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useCreate&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;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useHistory&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;UserRecord&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;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setErrors&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ResultErrorRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;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;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;createUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;async&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;await&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&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;setErrors&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
      &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;createUserApi&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;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RouteUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siteMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myProfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&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;resultObject&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&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;ResultRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserRecord&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;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&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;setErrors&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;errors&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
      &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;There was an issue signing up.&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="k"&gt;finally&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;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAwaiter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;createUserApi&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="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;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c-login-form&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;InputFormField&lt;/span&gt;
        &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;InputTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&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;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setUser&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="kd"&gt;with&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&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;value&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;
        &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SignUp.Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;InputFormField&lt;/span&gt;
        &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;InputTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&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;password&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setUser&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="kd"&gt;with&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&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;value&lt;/span&gt; &lt;span class="p"&gt;})}&lt;/span&gt;
        &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SignUp.Password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createUser&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;Sign&lt;/span&gt; &lt;span class="nx"&gt;Up&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;LoadingSpinner&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&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;
Strong typing is better than weak typing.





&lt;p&gt;Clean, concise, and strongly typed error handling in just 46 lines of code, including the UI.&lt;/p&gt;

&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;So how does this fancy-schmancy &lt;code&gt;Do.try&lt;/code&gt; work under the hood? By adding an abstraction on top of regular old &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;Promise&lt;/a&gt;s. Let’s break it down.&lt;/p&gt;

&lt;p&gt;First, let’s define some utility types we’re going to need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Represents an asynchronous method reference.
 */&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AsyncWorkload&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Handler for a typed error ResultRecord, or any type if a Javascript error occurred.
 */&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;CatchResultHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&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;ResultRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&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;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&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;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Handler for Do.try().finally(); Runs whether an error occurred or not.
 */&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;FinallyHandler&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="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Strongly typed handlers for strongly typed errors.





&lt;p&gt;Next, let’s take a look at our constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;void&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;private&lt;/span&gt; &lt;span class="na"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TReturnVal&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;private&lt;/span&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AsyncWorkload&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TReturnVal&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;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;workload&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;
Private constructor? 🤔





&lt;p&gt;That &lt;code&gt;private constructor&lt;/code&gt; is no mistake. You’ll notice in the previous snippets, usage of this pattern starts with &lt;code&gt;Do.try&lt;/code&gt;; that’s because &lt;code&gt;try&lt;/code&gt; is a static factory method that returns an instance of &lt;code&gt;Do&lt;/code&gt;. The &lt;code&gt;private constructor&lt;/code&gt; can only be called internally to the class, by the &lt;code&gt;try&lt;/code&gt; method. The implementation of &lt;code&gt;try&lt;/code&gt; is very straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;void&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;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="na"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AsyncWorkload&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TReturnVal&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;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&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;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&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;workload&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;
You’ll never know unless you… try.





&lt;p&gt;The &lt;code&gt;finally&lt;/code&gt; method is just as straightforward, with one important caveat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;void&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;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="na"&gt;finallyHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FinallyHandler&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finallyHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&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;
Pay special attention to the return value.





&lt;p&gt;Notice the return value, &lt;code&gt;return this;&lt;/code&gt; This allows for method chaining, i.e. &lt;code&gt;Do.try(workload).catch(catchHandler).finally(finallyHandler);&lt;/code&gt; In this code, &lt;code&gt;catch&lt;/code&gt; and &lt;code&gt;finally&lt;/code&gt; are both called on the same instance of &lt;code&gt;Do&lt;/code&gt; which is returned from &lt;code&gt;Do.try&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There’s also a &lt;code&gt;getAwaiter&lt;/code&gt; method, which allows us to &lt;code&gt;await&lt;/code&gt; for the result. All we need to do is return the internal promise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;void&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;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getAwaiter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TReturnVal&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;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&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;
This just allows us to await the result of the entire method chain.





&lt;p&gt;Now let’s get to the interesting part; the &lt;code&gt;catch&lt;/code&gt; method. Inside the catch method, we’re going to &lt;a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards"&gt;type guard&lt;/a&gt; the thrown object; if the thrown object is a &lt;code&gt;ResultRecord&lt;/code&gt; instance, we cast it as such and pass it as the catch handler’s first argument; otherwise, it’s some unknown error, so we pass it as the catch handler’s second argument. We also need to cast the promise back to a &lt;code&gt;Promise&amp;lt;TReturnVal&amp;gt;&lt;/code&gt; because of the return type of &lt;code&gt;Promise.catch&lt;/code&gt;, but the promise is still a valid &lt;code&gt;Promise&amp;lt;TReturnVal&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;void&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;public&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="na"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CatchResultHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&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;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;// check if thrown object is a ResultRecord&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;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;ResultRecord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// pass the ResultRecord as the first parameter&lt;/span&gt;
        &lt;span class="nx"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&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="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// otherwise, pass the generic error as the second parameter&lt;/span&gt;
      &lt;span class="nx"&gt;errorHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TReturnVal&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;// notice again, we are returning this to allow method chaining&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&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;
We just need to cast the promise back to a `Promise` because of the return type of `Promise.catch`, but rest assured, the promise is still a valid `Promise`.





&lt;p&gt;And there you have a basic implementation of a “maybe monad”. While the implementation here is an opinionated one, offering strongly typed error handling for &lt;code&gt;ResultRecord&lt;/code&gt; errors, you could easily implement the same thing for virtually any type you want to use to wrap up your errors, just as long as you’re able to implement a type guard for it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Taking It Further
&lt;/h1&gt;

&lt;p&gt;I think strongly typed error handling speaks enough for itself, but we can take it even further. This pattern enables an extremely powerful utility, and I think it’s the strongest argument for using it: &lt;a href="https://en.wikipedia.org/wiki/Default_(computer_science)#In_application_software"&gt;&lt;em&gt;default behavior&lt;/em&gt;&lt;/a&gt;. We can extend our &lt;code&gt;Do&lt;/code&gt; class to have a global configuration, allowing us to define &lt;em&gt;default behavior which is applied to every instance of &lt;code&gt;Do&lt;/code&gt; across the entire application&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;All we need to do is add a static configuration mechanism, and implement a check for our configuration inside the constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;DoTryConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * A default handler that will always run on error, if configured,
   * even if a `catch()` does not exist in the call chain.
   * This is useful for adding default error handling in the
   * development environment, such as `console.error(err)`.
   */&lt;/span&gt;
  &lt;span class="nl"&gt;defaultErrorHandler&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;CatchResultHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TResourceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TReturnVal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;void&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;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DoTryConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;defaultErrorHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AsyncWorkload&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TReturnVal&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;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;// check for defaultErrorHandler from config&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;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;ResultRecord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultErrorHandler&lt;/span&gt;&lt;span class="p"&gt;?.(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// rethrow so it doesn't interrupt call chain&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaultErrorHandler&lt;/span&gt;&lt;span class="p"&gt;?.(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// rethrow so it doesn't interrupt call chain&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
   * Sets the global configuration object for class {Do}
   * @param config the {DoTryConfig} object to set
   */&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;configure&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="nx"&gt;DoTryConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&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;
Default behavior is epic.





&lt;p&gt;So what does it look like to apply default behavior? Let’s contrive an example.&lt;/p&gt;

&lt;p&gt;We’re working on a large scale React application, and in order to aid debugging errors during development, we want to always log errors to the console in the development environment. Well, with the configuration mechanism we just added, it becomes trivially easy to add this default behavior. Just open up your &lt;code&gt;index.ts&lt;/code&gt; app entrypoint and add the handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.ts&lt;/span&gt;

&lt;span class="nx"&gt;EnvironmentUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runIfDevelopment&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;Do&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;defaultErrorHandler&lt;/span&gt;&lt;span class="p"&gt;:&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;ResultRecord&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&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;result&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;error&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;error&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;
Log errors to the console by default.





&lt;p&gt;You could use the same configuration mechanism to add default behavior to the &lt;code&gt;try&lt;/code&gt; or &lt;code&gt;finally&lt;/code&gt; portions of the call chain as well.&lt;/p&gt;

&lt;p&gt;The syntax is quite nice to read and easy to understand at a glace, but with the added bonus of having strongly typed errors, and optional default behavior.&lt;/p&gt;

&lt;p&gt;What do you think? Are you going to try “maybe monads” or the &lt;code&gt;Do.try&lt;/code&gt; pattern in your next TypeScript project?&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>design</category>
      <category>programming</category>
    </item>
    <item>
      <title>Protecting Your Privacy Online</title>
      <dc:creator>Mat Jones</dc:creator>
      <pubDate>Sat, 16 Jan 2021 14:29:23 +0000</pubDate>
      <link>https://dev.to/matjones/protecting-your-privacy-online-3bmc</link>
      <guid>https://dev.to/matjones/protecting-your-privacy-online-3bmc</guid>
      <description>&lt;p&gt;Do you know who’s tracking you?&lt;/p&gt;

&lt;p&gt;If you’re like most people, you use Google Chrome as your default browser, you use Google as your default search engine, and you don’t often think about online privacy much further than that. Why should huge tech companies make fortunes at the direct expense of their users’ privacy?&lt;/p&gt;

&lt;p&gt;There are a lot of ways you can protect yourself and your privacy online, if you care enough to make the effort.&lt;/p&gt;

&lt;h1&gt;
  
  
  Browser
&lt;/h1&gt;

&lt;p&gt;If you use Google Chrome as your default browser, &lt;em&gt;the browser itself&lt;/em&gt; is spying on you. Google wants to collect your user data so badly, &lt;a href="https://spreadprivacy.com/search-preference-menu-duckduckgo-elimination/" rel="noopener noreferrer"&gt;they won’t even let you use DuckDuckGo as your default search engine in most countries anymore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Last week, it was uncovered that the browser setting in Chrome to clear site data and cookies on exit &lt;a href="https://www.theverge.com/2020/10/21/21526341/google-chrome-local-storage-cookies-youtube-search-bug-fix" rel="noopener noreferrer"&gt;was exempting Google and YouTube from these requests&lt;/a&gt;. Reportedly, it was a bug, but as a software engineer who works daily with web technologies, I find it &lt;strong&gt;extremely&lt;/strong&gt; hard to believe that it really was a bug, and not just a sneaky, unethical feature that Google got caught on.&lt;/p&gt;

&lt;p&gt;If you value your privacy &lt;strong&gt;at all&lt;/strong&gt;, I highly recommend you switch to &lt;a href="https://www.mozilla.org/en-US/privacy/firefox/" rel="noopener noreferrer"&gt;Mozilla Firefox&lt;/a&gt;. Firefox is built with privacy in mind, and the browser code is &lt;a href="https://firefox-source-docs.mozilla.org/contributing/index.html" rel="noopener noreferrer"&gt;open source&lt;/a&gt;. On top of that, Firefox has better performance than Google Chrome.&lt;/p&gt;

&lt;h1&gt;
  
  
  Search Engine
&lt;/h1&gt;

&lt;p&gt;Please, stop using Google. Not only does Google track everything you search for and everything you click on in search results, but they also manipulate search results &lt;a href="https://www.businessinsider.com/google-manipulates-search-results-report-2019-11" rel="noopener noreferrer"&gt;for their own gain&lt;/a&gt; and to &lt;a href="https://www.breitbart.com/tech/2019/04/10/report-google-manually-manipulates-search-results-to-exclude-conservative-sites/" rel="noopener noreferrer"&gt;suppress political opposition&lt;/a&gt;. Further, Google tracks what you do &lt;em&gt;after you leave Google&lt;/em&gt;, too. &lt;a href="https://spreadprivacy.com/followed-by-ads/" rel="noopener noreferrer"&gt;Google trackers exist on 75% of the top million websites&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://duckduckgo.com/spread" rel="noopener noreferrer"&gt;DuckDuckGo&lt;/a&gt; is a privacy-focused search engine that never tracks you, never collects or sells user data, and strives to provide unbiased results. DuckDuckGo also offers a &lt;a href="https://duckduckgo.com/app" rel="noopener noreferrer"&gt;browser extension and a mobile browser&lt;/a&gt; to help protect yourself everywhere on the web.&lt;/p&gt;

&lt;h1&gt;
  
  
  Facebook
&lt;/h1&gt;

&lt;p&gt;My advice: just &lt;a href="https://www.facebook.com/help/224562897555674/" rel="noopener noreferrer"&gt;delete your Facebook account&lt;/a&gt;. Facebook is one of the worst offenders, if not &lt;em&gt;the&lt;/em&gt; worst offender. Same goes for any Facebook-owned companies, such as Instagram and WhatsApp.&lt;/p&gt;

&lt;p&gt;In 2016, Facebook &lt;a href="https://en.wikipedia.org/wiki/Facebook%E2%80%93Cambridge_Analytica_data_scandal" rel="noopener noreferrer"&gt;leaked millions of users’ private data to British consulting firm Cambridge Analytica without the users’ consent&lt;/a&gt;. This data &lt;strong&gt;was personally identifiable&lt;/strong&gt;, and primarily used for targeted political advertising.&lt;/p&gt;

&lt;p&gt;In 2019, Facebook ran a program to trick people into giving up all their data willingly by &lt;a href="https://www.techfoe.com/2019/01/facebook-is-paying-people-to-install.html" rel="noopener noreferrer"&gt;paying users to install a custom Facebook VPN app called Onavo&lt;/a&gt;, which allowed Facebook to just collect all meaningful data passing through the device, whether Facebook-related or not. This was a violation of Apple’s developer rules, so Onavo was quickly removed from the app store.&lt;/p&gt;

&lt;p&gt;If you still aren’t ready to give up on Facebook, at least install the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/facebook-container/" rel="noopener noreferrer"&gt;Facebook Container&lt;/a&gt; extension for Firefox, built by Mozilla. Facebook Container makes Facebook run inside a special sandboxed container, making it hard for Facebook to track you to external websites or use 3rd party cookies.&lt;/p&gt;

&lt;p&gt;If you’re looking for a secure and private messaging alternative to WhatsApp, I recommend &lt;a href="https://telegram.org/" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; or &lt;a href="https://www.signal.org/" rel="noopener noreferrer"&gt;Signal&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Browser Extensions
&lt;/h1&gt;

&lt;p&gt;If you’re looking to take privacy more seriously, there are several browser extensions I use and recommend for blocking trackers, ads, and other malware. All of these extensions are available for both Chrome and Firefox. They’re also all open source, meaning anyone can view or submit patches for the code. Open source is &lt;a href="https://dwheeler.com/secure-programs/Secure-Programs-HOWTO/open-source-security.html" rel="noopener noreferrer"&gt;generally considered good for security&lt;/a&gt;. In software development, &lt;a href="https://en.wikipedia.org/wiki/Linus%27s_law" rel="noopener noreferrer"&gt;Linus’s Law&lt;/a&gt; states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open source projects of sufficient size and traction have potentially thousands of contributors and testers actively working to find and resolve bugs and vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  DuckDuckGo Privacy Essentials
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/duckduckgo-for-firefox/" rel="noopener noreferrer"&gt;Download&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tired of being tracked online? We can help. At DuckDuckGo, we believe online privacy should be simple.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;  Blocks hidden trackers&lt;/li&gt;
&lt;li&gt;  Enforce HTTPS encrypted connections where possible&lt;/li&gt;
&lt;li&gt;  Gives each website a “Privacy Grade” (A-F) so you can see how protected you are at a glance&lt;/li&gt;
&lt;li&gt;  Option to send standardized &lt;a href="https://globalprivacycontrol.org/" rel="noopener noreferrer"&gt;Global Privacy Control&lt;/a&gt; signal to websites you visit&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/duckduckgo/duckduckgo-privacy-extension" rel="noopener noreferrer"&gt;Open source&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  uBlock Origin
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/" rel="noopener noreferrer"&gt;Download&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;uBlock Origin is &lt;strong&gt;not&lt;/strong&gt; an “ad blocker”, it’s a wide-spectrum content blocker with CPU and memory efficiency as a primary feature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;  Filters and blocks content based on block lists&lt;/li&gt;
&lt;li&gt;  Add additional popular community block lists&lt;/li&gt;
&lt;li&gt;  Block specific elements on a page manually or automatically&lt;/li&gt;
&lt;li&gt;  Add custom global or local filtering and blocking rules for content and/or Javascript&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/gorhill/uBlock" rel="noopener noreferrer"&gt;Open source&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Privacy Badger
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/privacy-badger17/" rel="noopener noreferrer"&gt;Download&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Privacy Badger automatically learns to block invisible trackers. Instead of keeping lists of what to block, Privacy Badger automatically discovers trackers based on their behavior.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;  Sends standardized &lt;a href="https://globalprivacycontrol.org/" rel="noopener noreferrer"&gt;Global Privacy Control&lt;/a&gt; and &lt;a href="https://www.eff.org/issues/do-not-track" rel="noopener noreferrer"&gt;Do Not Track&lt;/a&gt; signals to websites you visit&lt;/li&gt;
&lt;li&gt;  Blocks scripts that do not respect the Global Privacy Control or Do Not Track signals&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/EFForg/privacybadger" rel="noopener noreferrer"&gt;Open source&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  HTTPS Everywhere
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/https-everywhere/" rel="noopener noreferrer"&gt;Download&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Many sites on the web offer some limited support for encryption over HTTPS, but make it difficult to use. For instance, they may default to unencrypted HTTP, or fill encrypted pages with links that go back to the unencrypted site. The HTTPS Everywhere extension fixes these problems by rewriting all requests to these sites to HTTPS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;  Attempts to upgrade insecure HTTP connections to secure HTTPS connections&lt;/li&gt;
&lt;li&gt;  If HTTP→HTTPS upgrade is not possible, blocks the insecure HTTP connection by default&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://github.com/EFForg/https-everywhere" rel="noopener noreferrer"&gt;Open source&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ClearURLs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/clearurls/" rel="noopener noreferrer"&gt;Download&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This extension will automatically remove tracking elements from URLs to help protect your privacy when browsing through the Internet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;  Automatically removes URL tracking parameters like Google Analytics parameters and Amazon tracking parameters&lt;/li&gt;
&lt;li&gt;  Uses over 250 URL matching rules, matches a huge set of trackers&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://gitlab.com/KevinRoebert/ClearUrls" rel="noopener noreferrer"&gt;Open source&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  DNS
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffjyyuvz1hykxervd1c48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffjyyuvz1hykxervd1c48.png" alt="Statistics from my Technitium DNS server"&gt;&lt;/a&gt;&lt;/p&gt;
Real stats from my Technitium DNS server.



&lt;p&gt;If you want to take your privacy one step further, you can prevent your Internet Service Provider (ISP) from detecting every single site you request by running your own custom, encrypted &lt;a href="https://www.lifewire.com/what-is-a-dns-server-2625854" rel="noopener noreferrer"&gt;DNS server&lt;/a&gt;. I personally use and recommend &lt;a href="https://technitium.com/dns/" rel="noopener noreferrer"&gt;Technitium DNS&lt;/a&gt;, a free and open source DNS server.&lt;/p&gt;

&lt;p&gt;Technitium allows you to enforce that all DNS queries are encrypted, either via &lt;a href="https://en.wikipedia.org/wiki/DNS_over_TLS" rel="noopener noreferrer"&gt;DNS-over-TLS&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/DNS_over_HTTPS" rel="noopener noreferrer"&gt;DNS-over-HTTPS,&lt;/a&gt; which prevents your ISP from being able to see all of your DNS requests unencrypted. Additionally, you can block ads and malware that may have slipped through your other defenses by using a &lt;a href="https://blog.technitium.com/2018/10/blocking-internet-ads-using-dns-sinkhole.html" rel="noopener noreferrer"&gt;DNS Sinkhole&lt;/a&gt;. Basically, if a domain is requested which is in any of your configured block lists, Technitium just resolves the IP of the domain to an invalid IP address, 0.0.0.0.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fod866qvj9cjggbkpsit4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fod866qvj9cjggbkpsit4.png" alt="DNS blocked domains"&gt;&lt;/a&gt;&lt;/p&gt;
Top domains blocked over the last one week by my Technitium DNS server



&lt;h1&gt;
  
  
  VPN
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8lbyl028lyjx961qbgr1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8lbyl028lyjx961qbgr1.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
Always use protection.



&lt;p&gt;If you want to truly be fully anonymous online, you’ll need to use a 3rd party &lt;a href="https://en.wikipedia.org/wiki/Virtual_private_network" rel="noopener noreferrer"&gt;VPN&lt;/a&gt; service. However, not all VPNs are created equal; you need to make sure you choose a VPN provider with a strict no-logging policy. I use and can recommend &lt;a href="https://nordvpn.com/features/strict-no-logs-policy/" rel="noopener noreferrer"&gt;NordVPN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;NordVPN has a strict no-logging policy, and this policy has been verified by independent auditor PricewaterhouseCoopers AG, one of the &lt;a href="https://en.wikipedia.org/wiki/Big_Four_accounting_firms" rel="noopener noreferrer"&gt;Big Four auditing firms&lt;/a&gt;. NordVPN &lt;a href="https://support.nordvpn.com/General-info/Features/1061811142/Jurisdiction-we-operate-in.htm" rel="noopener noreferrer"&gt;operates within the jurisdiction of Panama&lt;/a&gt;, which has no legislation regarding mandatory data retention, unlike the US and EU; this is a good thing for a VPN provider. Such a strict no-logging policy could not exist legally under US or EU jurisdiction. You can even pay for your NordVPN subscription with cryptocurrency, without ever leaving a trace of your real identity.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Onion Router (Tor)
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffkarzixxtgxoyrwkmhgo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffkarzixxtgxoyrwkmhgo.png" alt="Tor logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to go &lt;a href="https://en.wikipedia.org/wiki/Edward_Snowden" rel="noopener noreferrer"&gt;Edward Snowden&lt;/a&gt; level private, NordVPN also has a feature called &lt;a href="https://nordvpn.com/features/onion-over-vpn/" rel="noopener noreferrer"&gt;Onion Over VPN&lt;/a&gt;. When connected to an Onion Over VPN server, all your internet traffic gets routed through a VPN server, then through a &lt;a href="https://www.torproject.org/" rel="noopener noreferrer"&gt;Tor&lt;/a&gt; proxy, and only then reaches the Internet. You can, of course, just use Tor directly via the &lt;a href="https://www.torproject.org/download/" rel="noopener noreferrer"&gt;Tor Browser&lt;/a&gt; without a VPN at all, but this still opens you up to some vulnerabilities.&lt;/p&gt;

&lt;p&gt;If you use Tor without a VPN, your ISP will know you are using Tor to access the internet, though this alone doesn’t compromise the actual data being sent and received over Tor. However, your IP address can still be compromised if there is a snooper, or multiple snoopers, in the network.&lt;/p&gt;

&lt;p&gt;Tor node servers are volunteer-operated; anyone, including authorities or other bad actors, can set up and operate a Tor node. While no single node is able to see the full network request or response, someone operating multiple servers which your request/response is routed through — especially entry or exist nodes — can discover your real IP address with a little work. For this reason, I still recommend using a VPN provider with Tor. That way, if your IP address is compromised, it’s just a VPN IP address and no real identifying information is compromised.&lt;/p&gt;

&lt;h1&gt;
  
  
  “I have nothing to hide. Why should I care?”
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F806wxb0vir7sgo9tw5nf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F806wxb0vir7sgo9tw5nf.jpg" alt="Edward Snowden"&gt;&lt;/a&gt;&lt;/p&gt;
If you’re asking this, Edward Snowden is disappointed.



&lt;h2&gt;
  
  
  Privacy Is Your Right
&lt;/h2&gt;

&lt;p&gt;Privacy is a right, but it’s not one we’ve always had; in fact, people in several authoritarian nations around the world, people still don’t have the right to privacy. And it’s one of the most important rights, right behind the right to free speech. Those who came before us fought so that we could enjoy the right to privacy, and dismissing that is grossly ignorant of history and its importance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Data Has Value
&lt;/h2&gt;

&lt;p&gt;Google is &lt;a href="https://moneyinc.com/richest-companies-in-the-world-in-2019/" rel="noopener noreferrer"&gt;among the top 5 richest companies in the world&lt;/a&gt; when ranked by market cap. But the lion’s share of Google products are free; Gmail, Google Calendar, Google Drive, Google Photos, Google Meet, YouTube, etc. So how does Google make &lt;em&gt;so much money&lt;/em&gt;? Simple. They sell your personal user data to third parties. Why should a global mega-corporation be allowed to profit freely and massively from your personal user data?&lt;/p&gt;

&lt;h2&gt;
  
  
  You Have No Idea How Much Is Out There
&lt;/h2&gt;

&lt;p&gt;Most of the time, apathy about online privacy is actually a symptom of ignorance. Studies show that even those who are initially unbothered by online privacy issues &lt;a href="https://www.eff.org/files/willis_and_zeljkovic.pdf" rel="noopener noreferrer"&gt;become concerned when confronted with the amount and nature of data collection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If a stranger asked you to to fill out a 150 question form regarding personal information, with the express intention to sell that data to 3rd parties, would you consent?&lt;/p&gt;

&lt;h1&gt;
  
  
  Honorable Mentions
&lt;/h1&gt;

&lt;p&gt;There are several other privacy tools that I haven’t covered here, but are definitely worth an honorable mention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Password Managers
&lt;/h2&gt;

&lt;p&gt;Everyone should be using a password manager. Reusing a password for multiple accounts is a security risk. A decent password manager can automatically detect signup forms in your browser and offer to generate a secure, strong password for you, sync your passwords between devices, and warn you if you have any weak, compromised, or reused passwords. I personally use a &lt;a href="https://1password.com/" rel="noopener noreferrer"&gt;1Password&lt;/a&gt; family account at home and a team account at work, and I can confidently recommend 1Password, but &lt;a href="https://www.dashlane.com/" rel="noopener noreferrer"&gt;Dashlane&lt;/a&gt; is another great option. If you’re looking for a free and completely offline solution, you can check out &lt;a href="https://keepassxc.org/" rel="noopener noreferrer"&gt;KeePassXC&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://privacytools.io/" rel="noopener noreferrer"&gt;PrivacyTool.io&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Privacy-respecting search engine to find more privacy tools&lt;/li&gt;
&lt;li&gt;  Privacy-focused services and software&lt;/li&gt;
&lt;li&gt;  Non-profit and open source&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://joindeleteme.com/" rel="noopener noreferrer"&gt;DeleteMe&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Automatically opt-out of the sale of your personal information by over 40 of the biggest data brokers&lt;/li&gt;
&lt;li&gt;  Provides DIY guides to manually opt-out for free&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://haveibeenpwned.com/" rel="noopener noreferrer"&gt;haveibeenpwned.com&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Historical records of data breaches&lt;/li&gt;
&lt;li&gt;  Check if any accounts associated with your email address have been compromised&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>privacy</category>
      <category>security</category>
      <category>offthegrid</category>
    </item>
  </channel>
</rss>
