<?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: John Colagioia (he/him)</title>
    <description>The latest articles on DEV Community by John Colagioia (he/him) (@jcolag).</description>
    <link>https://dev.to/jcolag</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%2F349233%2F9aa9cddf-b6e0-46e9-84f9-4891041fcb5b.png</url>
      <title>DEV Community: John Colagioia (he/him)</title>
      <link>https://dev.to/jcolag</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jcolag"/>
    <language>en</language>
    <item>
      <title>Frameworks of the Future?</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Wed, 21 Jul 2021 12:29:06 +0000</pubDate>
      <link>https://dev.to/jcolag/frameworks-of-the-future-3605</link>
      <guid>https://dev.to/jcolag/frameworks-of-the-future-3605</guid>
      <description>&lt;p&gt;Something has been nagging me in the back of my mind for a while:  Where are the "modern" web frameworks?&lt;/p&gt;

&lt;p&gt;What I mean is that---apologies for the history lesson---once &lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; got its footing, it did enough that it changed how web development happen.  Web applications in the 1990s was a nightmare, hacking everything together through &lt;a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface"&gt;CGI&lt;/a&gt; and &lt;code&gt;session&lt;/code&gt; variables, which didn't &lt;em&gt;substantially&lt;/em&gt; change until Rails.&lt;/p&gt;

&lt;p&gt;Today, most of that mess is just how you threaten children to behave, because everybody else now has their equivalent to Rails.  C#/VB.NET's &lt;a href="https://dotnet.microsoft.com/apps/aspnet"&gt;ASP.NET&lt;/a&gt;, Elixir's &lt;a href="https://www.phoenixframework.org/"&gt;Phoenix&lt;/a&gt;, Go's &lt;a href="https://beego.me/"&gt;Beego&lt;/a&gt;, PHP's &lt;a href="https://laravel.com/"&gt;Laravel&lt;/a&gt;, Haskell's &lt;a href="https://ihp.digitallyinduced.com/"&gt;IHP&lt;/a&gt;, Java's &lt;a href="https://jakarta.ee/"&gt;Jakarta EE&lt;/a&gt;, JavaScript's &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt;, and literally dozens of others now have effective parity with Rails.&lt;/p&gt;

&lt;p&gt;I was also struck by a coincidence:  I picked up Rails about fourteen years ago, and wrote my first (awful) web application about fourteen years before &lt;em&gt;that&lt;/em&gt;, and yes, I'm really that old...But that triggered the question.  If web development in 1993 looked like it would in 2007 (except for still-obscure Rails), and Rails looked like web development today, is there a framework out there that has all the features that we'll demand as necessary in 2035?&lt;/p&gt;

&lt;p&gt;I'm not looking for the overthrow of CRUD-through-MVC.  Rather, &lt;a href="https://github.com/hobo/hobo"&gt;Hobo&lt;/a&gt; and &lt;a href="https://github.com/hoodiehq/hoodie"&gt;Hoodie&lt;/a&gt; seemed like advances---Hobo was sort of Rails for Rails, and Hoodie was an offline-first framework for something like what we now call Progressive Web Apps---when I tried them early in their life-cycle, but both seem to have withered away.  And nobody else (that I can find) seems interested in improving graphical design (as in, "just use Material Design, or Carbon, or whatever"), cleaner parent/child relationships, automatically updating views and controllers to match changes to the models, and probably features that I don't know that I need.&lt;/p&gt;

&lt;p&gt;I asked a similar question in a different community, and the closest they came up with was the niche &lt;a href="https://beam.apache.org/"&gt;Apache Beam&lt;/a&gt; and the obligatory vague hand-waving about no-code systems.  So, maybe DEV seeming to skew younger and more deliberately technical might get a better view of things?  Is anybody using a "Framework of the Future" that we should all know about?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
      <category>rails</category>
    </item>
    <item>
      <title>How Long Will That Project Take?</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Thu, 28 Jan 2021 08:51:23 +0000</pubDate>
      <link>https://dev.to/jcolag/how-long-will-that-project-take-1163</link>
      <guid>https://dev.to/jcolag/how-long-will-that-project-take-1163</guid>
      <description>&lt;p&gt;As a quick note, I &lt;a href="https://john.colagioia.net/blog/2021/01/27/estimate.html"&gt;released this post&lt;/a&gt; (well, a version with slightly different editing) on my blog, Wednesday morning, so it can get to be (as I tend to be) a bit rambling in places.  However, the original text is &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;on GitHub&lt;/a&gt; (licensed CC-BY-SA), so if anything seems muddy, by all means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave a comment here,&lt;/li&gt;
&lt;li&gt;Leave a comment on the blog,&lt;/li&gt;
&lt;li&gt;File an issue on GitHub, or&lt;/li&gt;
&lt;li&gt;Add a pull request!&lt;/li&gt;
&lt;/ul&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--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jcolag"&gt;
        jcolag
      &lt;/a&gt; / &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;
        entropy-arbitrage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      John Colagioia's Blog Posts
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 




&lt;p&gt;Every once in a while, I’m reminded that one of the most important tasks in software development that most developers never learn to do is to estimate projects. Interestingly, rather than drawing from other disciplines to teach people how to produce better estimates, the industry has largely shifted to management techniques that assume that estimates are going to be embarrassingly wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dKq_Lkvm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4d4brvdhuzqsssn3xpz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dKq_Lkvm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4d4brvdhuzqsssn3xpz8.png" alt="Preparing to Estimate" title="Preparing to Estimate" width="740" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They don't need to be.&lt;/p&gt;

&lt;p&gt;So, since the topic has come up a couple of times in the past few weeks, I wanted to centralize my thoughts, in case they’re useful to someone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estimating or Guessing
&lt;/h2&gt;

&lt;p&gt;To start off, let’s be clear that a project estimate isn’t hearing a quick description of the finished project and picking a number that “sounds about right.”  That’s a &lt;em&gt;guess&lt;/em&gt;. There are certainly cases where guesses are acceptable, and your guess might sometimes be right, but estimates are something different.&lt;/p&gt;

&lt;p&gt;Let’s start with the dumb “the dictionary defines…” trope, here, borrowing from &lt;a href="https://en.wiktionary.org/wiki/estimate"&gt;Wiktionary&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;estimate&lt;/strong&gt; (third-person singular present &lt;strong&gt;estimates&lt;/strong&gt; , present participle &lt;strong&gt;estimating&lt;/strong&gt; , simple past and past participle &lt;strong&gt;estimated&lt;/strong&gt; )&lt;/p&gt;

&lt;p&gt;1&lt;/p&gt;

&lt;p&gt;To calculate roughly, often from imperfect data.&lt;/p&gt;

&lt;p&gt;2&lt;/p&gt;

&lt;p&gt;To judge and form an opinion of the value of, from imperfect data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those are basically the same thing in different contexts, and I hope it shows the distinction between estimating and guessing. Your estimate &lt;em&gt;would be&lt;/em&gt; the correct answer, if you had better information. Your guess would probably not change, however, if more information was available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fudge Factors
&lt;/h2&gt;

&lt;p&gt;While, again, guessing is fine in certain environments, it has a huge weakness: To try not to over-promise, workers (&lt;em&gt;especially&lt;/em&gt; software developers, in my experience) tend to try to find some magic &lt;strong&gt;multiplier&lt;/strong&gt; that will turn their fantasy numbers into a valid estimates. It doesn’t work, being a perfect example of "&lt;a href="https://en.wiktionary.org/wiki/garbage_in,_garbage_out"&gt;garbage in, garbage out&lt;/a&gt;," but that doesn’t stop people from hoping to make the proposed time-frame long enough to sound credible, but short enough for the investment to sound like a good bargain.&lt;/p&gt;

&lt;p&gt;In my experience, these factors come in a small handful of varieties, some more legitimate than others.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After reviewing the plan, you can point to the places where the project has a lot of uncertainty that could go horribly wrong, and don’t think that there’s any way to measure that impact without doing the work. For example: 

&lt;ul&gt;
&lt;li&gt;If you need to integrate with a third-party service that’s still under development, you’re going to spend an unknown amount of time testing with your counterparts, discussing options in conference calls, and rewriting code as the target moves.&lt;/li&gt;
&lt;li&gt;If you need to optimize an algorithm to trim requests below a certain threshold, you can (and should) calculate a lower-bound for the run-time, but time spent actually trimming time can easily hit a point of diminishing returns, dragging out the time required.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;You’re including overhead of work that you don’t personally do, but is an unavoidable part of the project. I’ll talk about this more, later.&lt;/li&gt;
&lt;li&gt;You haven’t the foggiest idea of what’s involved in this project, so you picked a number out of a hat and feel that you need a fudge factor, just in case you’re wrong; you’re probably also planning to work late when the deadline approaches to meet the deadline. This is the situation that I described earlier, where developers tend to just find a compromise between sounding appealing and sounding possible.&lt;/li&gt;
&lt;li&gt;You don’t trust the other party to uphold their end of your agreement, so you’re just flat-out assuming that you’re going to need to do everything multiple times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Uncertainty in the plan (the first item) is almost always a legitimate reason to “pad” a schedule. The padding is localized on the schedule and each instance has specific justifications, so it’s possible to isolate that part of the project.&lt;/p&gt;

&lt;p&gt;Overhead—design, management, testing, debugging—is also generally going to be legitimate, and probably even good to consider, as long as you understand the different roles involved in the project and which need to be part of your estimate.&lt;/p&gt;

&lt;p&gt;You see the “fudge factor” in cases where the developer doesn’t understand how to build a real estimate. You’ll see people talk about “rules” of multiplying all initial guesses by two or three, and we all start our careers thinking that's how you do the job. But it’s basically an admission of being far too optimistic, as well as showing ignorance of scheduling, so it’s probably best to steer clear of doing this, unless you can be specific about it, as I'll get to later. Basically, this multiplier is a step away from “it’ll get done when it’s done.”&lt;/p&gt;

&lt;p&gt;Finally, trust is, as they say, a two-way street. In one direction, you have developers padding schedules because they believe that management or the customer is going to change the scope or even the nature of the project, meaning that someone will need to scrap or rework a fair amount of existing work. In the other direction, management or customers might push to pad the schedule (or might treat the budget for the schedule as if it was several times larger), because they don’t trust the developer(s) to do the right work. If you’re adjusting the schedule because of a trust problem, you need to build trust, rather than tweak the schedule, or your schedules are always going to leave someone looking foolish.&lt;/p&gt;

&lt;p&gt;Note, by the way, that Agile development processes have tried to take trust out of the process, by shifting the focus of planning to “sprints,” when developers just try to get through as many tickets as possible, and then use that data point to predict how many "points" the team will complete during the next sprint. In doing that, however, it also mostly eliminates the idea of rigorous estimates in favor of gut reactions.&lt;/p&gt;

&lt;p&gt;As I said about guessing above, there are contexts where guessing is fine, and Agile development processes are (usually) one of them. Just be mindful about what you’re doing and the risk that’s introduced, compared with how that risk is contained.&lt;/p&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;p&gt;Now that we better understand what &lt;em&gt;not&lt;/em&gt; to do and why we might care about getting accurate estimates, let’s talk about how to put a credible estimate together. Improved estimates are important, because reliably getting work done on time helps to fix the trust issue discussed above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Divide and Conquer
&lt;/h3&gt;

&lt;p&gt;That all said, the way I build my own estimates to increase the chances of it being correct is to recursively break tasks down with best-case and worst-case estimates, until the worst-case estimate is no more than a few hours long.  I pick that size, since just about anybody is going to get that scale right, even if it’s just a guess.&lt;/p&gt;

&lt;p&gt;That maximum-size time (of a few hours) is based on projects that are likely to take an on-one-hand number of worker-months, and so should probably be adjusted if that’s not the likely scale. Otherwise, you’re mapping out individual hours on a twenty worker-year project or half-days on a weekend hack, which is either a waste of time or too abstract to help. To adjust, if we figure that three months has around five hundred work hours in them, you’re talking about your smallest unit being a bit less than 1% of the guessed-at size of the whole project.  If you get that guess wrong, no harm done if your minimum size gives you three times as many sub-tasks.&lt;/p&gt;

&lt;p&gt;By contrast, looking for the worst case for each sub-task keeps you honest and reminds you about all the little tasks and pitfalls that you were going to just assume happened magically. Don’t skip that part.&lt;/p&gt;

&lt;p&gt;A plan like this might look something like the following excerpt. Let’s say that the project is a traditional blog.  Ignore the fact that the estimates assume a multi-month project, when the reality is that you'll probably be done in about a week.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task Name&lt;/th&gt;
&lt;th&gt;Best Case&lt;/th&gt;
&lt;th&gt;Worst&lt;/th&gt;
&lt;th&gt;Actual&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Index&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;👇2 h&lt;/td&gt;
&lt;td&gt;👇5 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 List Posts&lt;/td&gt;
&lt;td&gt;30 m&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Paginate&lt;/td&gt;
&lt;td&gt;1.5 h&lt;/td&gt;
&lt;td&gt;4 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create Post&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;👇21 h&lt;/td&gt;
&lt;td&gt;👇26 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Implement Default Editor (Scaffolding)&lt;/td&gt;
&lt;td&gt;0 h&lt;/td&gt;
&lt;td&gt;0 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Evaluate Rich-Text Editors&lt;/td&gt;
&lt;td&gt;16 h&lt;/td&gt;
&lt;td&gt;16 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Replace Default Editor with Rich Text&lt;/td&gt;
&lt;td&gt;2 h&lt;/td&gt;
&lt;td&gt;3 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Validate Input&lt;/td&gt;
&lt;td&gt;2 h&lt;/td&gt;
&lt;td&gt;4 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Preview Post&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Save Post&lt;/td&gt;
&lt;td&gt;0 h&lt;/td&gt;
&lt;td&gt;2 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Edit Post&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;👇1 h&lt;/td&gt;
&lt;td&gt;👇6 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Load Existing Post to Create Page&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;4 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Overwrite Existing Post on Save&lt;/td&gt;
&lt;td&gt;0 h&lt;/td&gt;
&lt;td&gt;2 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Display Post&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;👇2 h&lt;/td&gt;
&lt;td&gt;👇7 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Convert Internal Representation to HTML&lt;/td&gt;
&lt;td&gt;0 h&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Insert CSS Classes into Post&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;4 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Show Post in Context&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;2 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Add Comments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;👇3.5 h&lt;/td&gt;
&lt;td&gt;👇6 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Show Existing Comments for Post (No Threading)&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Create Comment Form on Post Page&lt;/td&gt;
&lt;td&gt;2 h&lt;/td&gt;
&lt;td&gt;4 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👆 Save Comment Associated with Post&lt;/td&gt;
&lt;td&gt;30 m&lt;/td&gt;
&lt;td&gt;1 h&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I didn’t bother to break up &lt;em&gt;Evaluate Rich-Text Editors&lt;/em&gt;, because (a) this isn’t a real project, (b) breaking it down would probably just mean listing the candidates, and (c) it’s not out of the question to say “after I’ve spent two days on this, I’m just going to pick whatever I’ve seen that looks best, and forget the rest.”&lt;/p&gt;

&lt;p&gt;Anyway, this project outline gives us a fifty-hour prediction. As tasks are completed, fill out the actual time spent in the fourth column, as honestly as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Account for Overhead
&lt;/h3&gt;

&lt;p&gt;Next, take your development-time estimate and put together a 2:1:3 schedule of design/architecture time (2x), development time (your actual estimate), and quality assurance/debug time (3x). I forget where I got that guideline from, honestly, but it’s basically fixed overhead that you don’t want to think about up-front.&lt;/p&gt;

&lt;p&gt;I’ve never seen anybody successfully cheat that ratio, by the way. I've seen many &lt;em&gt;attempts&lt;/em&gt;, but they invariably slow the project down, rather than speeding it up. You can push the work around—like having the development team use test-driven development—and many projects can often afford to ship incomplete. But as a sell-able product, you need that design and test time, even though it might feel wasteful.&lt;/p&gt;

&lt;p&gt;That is, for the blog plan described above that’s planned to take fifty hours, the design time (all choices, coordination, and prototyping) is going to take approximately one hundred hours, and testing/debugging time is going to come in at around one hundred fifty hours, for a total of three hundred worker-hours to make this a polished product.&lt;/p&gt;

&lt;p&gt;Well, that’s not actually &lt;em&gt;entirely&lt;/em&gt; true. That sixteen-hour step to evaluate rich-text editors isn’t a development task, even though a developer is going to do that work. Instead, it’s a design task that almost certainly incurs no QA time. So as you get better at this, you might say that the real development schedule is thirty-four hours, with sixty-eight plus sixteen hours of design time, and about a hundred hours of testing and debugging.&lt;/p&gt;

&lt;p&gt;That's still a lot, but...well, I'll get to that later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feedback
&lt;/h3&gt;

&lt;p&gt;As you work, you then track real time elapsed against your low-level estimates, in order to forecast how you should adjust estimates in the future. Divide the worst-case estimates by the actual time spent. Take the average of those ratios—that’s not &lt;em&gt;quite&lt;/em&gt; mathematically valid, but it’s close enough for our purposes—and use that final ratio to adjust the sub-task estimates on the next project.&lt;/p&gt;

&lt;p&gt;Note that this isn’t a “fudge factor” like the above. This is a recognition that you’re still learning to get the small estimates right, so this is deliberately correcting for your bias. As you go through more projects, you’ll notice that the ratio gets close enough to one-to-one that you won’t need to correct for bias.&lt;/p&gt;

&lt;p&gt;Don’t stop &lt;em&gt;tracking&lt;/em&gt; the bias, though. If it starts drifting, again, you’re going to want to know that and start accounting for it.  In some extreme cases, you might want to make sure that you're not burning out or otherwise unhappy, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Viability
&lt;/h2&gt;

&lt;p&gt;Yes, you’re thinking that this is much more work for a proposal than “eh, sounds like a nine-month job to me,” but it’s work that makes it easy to get estimates &lt;em&gt;pretty&lt;/em&gt; close to the actual work that’ll be done, and also quickly see how any scope changes are going to impact the schedule. That builds everybody’s confidence, which is helpful.&lt;/p&gt;

&lt;p&gt;Because this gives you a good view on the cost of features, a process like this also helps argue for and against work based on the return on investment, which is more useful than just arguing that a project will be “good” for the product.  For example, our estimate for that blog came to hundreds of hours of work, which would easily cost the organization tens of thousands of dollars.  Is that money going to bring the organization more value than installing WordPress?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;However&lt;/em&gt;, like I hinted a couple of times, this process doesn’t mesh particularly well with Agile-style project planning, because Agile is built around the idea that long-term estimates are going to be wrong, so take all of this with a grain of salt for a modern office. I suspect that there’s probably a way to fit the two worldviews together, but I haven’t gotten it to work, yet, beyond mapping out individual tickets.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is &lt;a href="https://pxhere.com/en/photo/819794"&gt;untitled&lt;/a&gt; by an unknown PxHere photographer, released under the terms of the &lt;a href="https://creativecommons.org/publicdomain/zero/1.0/"&gt;Creative Commons CC0 1.0 Universal Public Domain Dedication&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Impostor Syndrome</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Sun, 23 Aug 2020 11:07:55 +0000</pubDate>
      <link>https://dev.to/jcolag/impostor-syndrome-1208</link>
      <guid>https://dev.to/jcolag/impostor-syndrome-1208</guid>
      <description>&lt;p&gt;As a quick note, I &lt;a href="https://john.colagioia.net/blog/2020/08/23/imposter.html"&gt;released this post&lt;/a&gt; (well, a version with slightly different editing) on my blog, Sunday morning, so it can get to be (as I tend to be) a bit rambling in places.  However, the original text is &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;on GitHub&lt;/a&gt; (licensed CC-BY-SA), so if anything seems muddy, by all means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave a comment here,&lt;/li&gt;
&lt;li&gt;Leave a comment on the blog,&lt;/li&gt;
&lt;li&gt;File an issue on GitHub, or&lt;/li&gt;
&lt;li&gt;Add a pull request!&lt;/li&gt;
&lt;/ul&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/jcolag"&gt;
        jcolag
      &lt;/a&gt; / &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;
        entropy-arbitrage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      John Colagioia's Blog Posts
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 




&lt;p&gt;I've had several conversations, recently, about dealing with impostor syndrome, so I decided to collect my thoughts over the years, in case they're useful to someone down the line.  I didn't write this with the software industry &lt;em&gt;specifically&lt;/em&gt; in mind, but it's obviously a central part of the conversation for this community.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--erI5Uuhd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l8mau1xcnquimp8koq8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--erI5Uuhd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l8mau1xcnquimp8koq8k.png" alt="Nobody will notice..." title="Pug Impostor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  No Impostor Left Behind
&lt;/h2&gt;

&lt;p&gt;The thing I’ve found myself talking about recently—as you can probably tell by the Quora question titles above—to the point that it made sense to round up my thoughts on the matter, is &lt;a href="https://en.wikipedia.org/wiki/Impostor_syndrome"&gt;impostor syndrome&lt;/a&gt;, the feeling that we’re going to be revealed as frauds in our work.&lt;/p&gt;

&lt;p&gt;Most of the talk about this—studies, online articles, and so forth—focus on a subset of people who are sometimes &lt;em&gt;considered&lt;/em&gt; by the dominant group, such as women and racial/sexual minorities. There’s a good reason for that focus, of course. People who look like me rarely have our backgrounds questioned and there’s no history of shunting us into poorer education.&lt;/p&gt;

&lt;p&gt;However, doing so treats this as a problem &lt;em&gt;for&lt;/em&gt; women and minorities—something that separates them from everybody else at work—instead of what it really is: Something that everybody deals with, pretty much all the time. The reason you feel that way has nothing to do with the relative lengths of your chromosomes or your ethnicity.&lt;/p&gt;

&lt;p&gt;We all deal with it, because…&lt;/p&gt;

&lt;h2&gt;
  
  
  Work Isn’t...Real
&lt;/h2&gt;

&lt;p&gt;Well, that title’s an exaggeration. What I mean is that jobs are a &lt;em&gt;social construct&lt;/em&gt;, which are real things.  However, being artificial, none of us is born to do any job naturally. Likewise, no education or experience truly prepares you for the next emergency.&lt;/p&gt;

&lt;p&gt;So, we all feel like we’re impostors, because…well, we actually &lt;em&gt;are&lt;/em&gt; all impostors. We all pretend that we have the answers, knowing full well that we probably won’t actually have any solid answers until the job is done. We continue on after every failure, and we fail a lot—unavoidably—because we’re only human. And we scramble to figure things out as we go.&lt;/p&gt;

&lt;p&gt;So, yeah, we’re all impostors. Perfection isn’t worth the price. It’s not a bad thing, except to the extent that it bothers you.&lt;/p&gt;

&lt;p&gt;Keep in mind that this isn’t limited to work. Nobody actually fits completely into their social circle, either. Our relationships are never going to be perfect and there’s always something more we should probably be doing or getting, that we can’t even see. It’s the nature of the world, and a lot of growing up seems to involve coming to terms with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exceptions
&lt;/h2&gt;

&lt;p&gt;Well, sorry.  Yes, it’s not &lt;em&gt;all&lt;/em&gt; of us.  There’s a handful of completely terrible people in every field who are terrible at their jobs, of course, yet somehow spend their lives 100% sure they can (and should) do everything.  Learn to find them, and if you can find a way to get away with it, nobody’s going to think less of you for murdering them, because they somehow are never able to figure out that their confidence in themselves is grossly misplaced. ( &lt;strong&gt;Note/PSA&lt;/strong&gt; :  That’s a joke. Please don’t actually murder people.  It’s all sorts of bad. Definitely avoid them, though.)&lt;/p&gt;

&lt;p&gt;Some people will also have obscenely boring jobs, where they get to be confident that nothing new is going to come up.  If that person is you, leave that job as soon as you can manage.  No cutesy note, here.  Seriously, quit as soon as you realize that you have that job and can afford it. That’s a career-ender.&lt;/p&gt;

&lt;p&gt;And some of us don't really think about it.&lt;/p&gt;

&lt;p&gt;But apart from those sorts of people, pretty much anybody who tells you that they think they fit into a new group on their first day is lying.  Anybody who doesn’t have a learning curve when they start a new job is either lying or taking what have to be the most boring dead-end jobs available.&lt;/p&gt;

&lt;p&gt;My point is that it’s both laughable and hurtful to talk about—for an example I’m familiar with, because so much has been written on the topic—women in the software industry suffering with impostor syndrome. It makes them seem like the very outsiders they worry they are, rather than bringing them into the fold, where some of us just come to terms with the worry more than others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible Solutions
&lt;/h2&gt;

&lt;p&gt;So, yes, we’re all worse at our jobs than we would like to be and there’s a part of each of us worried that someone is going to notice at the worst possible time. And the problem isn’t so much that this is a possible problem than this is &lt;em&gt;your&lt;/em&gt; problem. That is, the effect the feeling has on you is what’s causing damage.&lt;/p&gt;

&lt;p&gt;Can it be overcome? I don’t know. I’ve read quite a few articles over the years, and they’re all pretty silly, with “solutions” ranging from telling people that you believe in them—in hopes of normalizing it enough to sound sincere, I suppose—to a victim-blaming system that talks about the worker assessing where they really &lt;em&gt;can&lt;/em&gt; improve.&lt;/p&gt;

&lt;p&gt;As you might guess, I have a different outlook on the topic.&lt;/p&gt;

&lt;p&gt;Instead, I would encourage you to come to terms with that anxiety and use it to your advantage.&lt;/p&gt;

&lt;p&gt;First, try to understand that others aren’t necessarily better than you, and certainly not to the extent that you think you see. I have this conversation with colleagues all the time, because people tend to think that I’m particularly good at my job, even though I know my limits. The biggest difference, as I explain, is that they only see my work when it’s &lt;strong&gt;complete&lt;/strong&gt; , whereas we can all see the flaws in our own work as we go. Anybody who sees the blind alleys I run down and obvious mistakes I consistently would realize that I’m not doing anything different except maybe trying &lt;em&gt;more&lt;/em&gt; dumb things than my colleagues so that I keep moving. All the most successful people I’ve worked with function pretty much the same way, in any job, not being afraid to work outside our limits.&lt;/p&gt;

&lt;p&gt;Second, realize that the ubiquity of impostor syndrome means that you have a support group of seven billion fellow people in need. Find people you trust and talk it out with them. Listen to their fears and validate the good that you’ve seen of their work. If they don’t volunteer it, ask them to assess where you are, in comparison to other people in similar jobs with backgrounds like yours. If you’re relatively privileged in society—like a White or Asian man in programming tends to be—take this a step further and be open about your own mistakes and things you needed to learn or re-learn. Normalize being imperfect, because there’s nothing more normal than that.&lt;/p&gt;

&lt;p&gt;To build on that idea, also normalize being &lt;em&gt;excited&lt;/em&gt; when someone tells you they’re not familiar with something, rather than merely being surprised. Many of us have become jaded by having information constantly thrown at us that we need to celebrate the process of seeking out what we missed more often. We need to normalize imperfection, self-education, and not feeling like you fit in, because it’s so normal that most of us ignore the feeling, like we ignore the air we breathe until it becomes a problem for us.&lt;/p&gt;

&lt;p&gt;And finally, if you can, embrace that worry. The best part of a lot of jobs can be finding your footing, taking on opportunities to grow quickly.  That nagging feeling inside you that something isn’t quite right can be a terrific compass to find those nooks and crannies.  That fear is telling you where the interesting stuff is going on.  If Sheryl Sandberg didn’t pirate the term for women giving up their lives to the corporate cause, I’d say it’s the sign to “&lt;a href="https://en.wikipedia.org/wiki/LeanIn.Org"&gt;lean in&lt;/a&gt;.”  So, I won’t say that.  Instead, I’ll just say that you have a tiny voice pointing you at interesting things to learn and opportunities to make things better.&lt;/p&gt;

&lt;p&gt;Above all, &lt;strong&gt;don’t&lt;/strong&gt; make it a contest. Given enough time and education, you can be better than anyone else at anything and anybody can be better than you. Acting like those temporary differences in skill are indicative of something deeper is just a waste of everybody’s energy.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is &lt;a href="https://wunderstock.com/photo/pug-imposter-pug-love_8426525097"&gt;Pug Imposter ‘Pug Love’&lt;/a&gt; by &lt;a href="https://www.flickr.com/photos/43810158@N07"&gt;DaPuglet&lt;/a&gt; on &lt;a href="https://wunderstock.com"&gt;Wunderstock&lt;/a&gt;, released under the terms of the &lt;a href="https://creativecommons.org/licenses/by-sa/2.0/"&gt;Creative Commons Attribution Share-Alike 2.0 Generic&lt;/a&gt; license.&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>Changing the Primary Git Branch</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Wed, 29 Jul 2020 10:55:02 +0000</pubDate>
      <link>https://dev.to/jcolag/changing-the-primary-git-branch-78n</link>
      <guid>https://dev.to/jcolag/changing-the-primary-git-branch-78n</guid>
      <description>&lt;p&gt;As a quick note, I &lt;a href="https://john.colagioia.net/blog/2020/07/29/gitmain.html"&gt;released this post&lt;/a&gt; on my blog, this morning, so it can get to be (as I tend to be) a bit rambling.  Oh, and the original text is &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;on GitHub&lt;/a&gt; (licensed CC-BY-SA), so if anything seems muddy, by all means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave a comment here,&lt;/li&gt;
&lt;li&gt;Leave a comment on the blog,&lt;/li&gt;
&lt;li&gt;File an issue on GitHub, or&lt;/li&gt;
&lt;li&gt;Add a pull request!&lt;/li&gt;
&lt;/ul&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/jcolag"&gt;
        jcolag
      &lt;/a&gt; / &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;
        entropy-arbitrage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      John Colagioia's Blog Posts
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 



&lt;p&gt;Recently, I decided to spend some time updating the repositories I use regularly to use a &lt;code&gt;main&lt;/code&gt; branch instead of &lt;code&gt;master&lt;/code&gt;, since the latter has unpleasant an alienating connotations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--glpzIV6M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0chbwszqauyyvhajb59p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--glpzIV6M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0chbwszqauyyvhajb59p.png" alt="Branches" title="Branches"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before you tell me that the term “obviously” derives from some different meaning, keep in mind that &lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt; takes a lot of its ideas from &lt;a href="https://en.wikipedia.org/wiki/BitKeeper"&gt;BitKeeper&lt;/a&gt;, which includes the concept of a master branch with slave branches. Likewise, if you’re going to argue that it’s from “master copy,” well, I have some awful news for you…&lt;/p&gt;

&lt;p&gt;Similarly, if you’re going to ask why it’s suddenly important…that’s actually a good question, because this sort of terminology should &lt;em&gt;never&lt;/em&gt; have been acceptable, and it’s embarrassing that we need to be reminded that our choice of words has consequences. But since everybody is thinking about it now, it’s a good time to do it, rather than sheepishly operating the minority of repositories that didn’t bother.&lt;/p&gt;

&lt;p&gt;In other words, the best time to fix a mistake is always &lt;strong&gt;now&lt;/strong&gt; , because you can’t fix anything before now and waiting until later just leaves the mistake where it is.&lt;/p&gt;
&lt;h2&gt;
  
  
  Altering the Branch
&lt;/h2&gt;

&lt;p&gt;Plus, regardless of the intent, it’s &lt;em&gt;still&lt;/em&gt; an uncomfortable term to have around, and it can easily give the impression that the project (and the industry as a whole) isn’t ready to welcome certain kinds of people. So, I’m going to change it where I can. Because I have dozens of repositories floating around, I decided to get it right once and then move the information to a script to make it reproducible when I stumble on another forgotten repository. Here’s the almost-final version of the script for GitHub, in case anybody wants to play along.&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;#!/bin/sh&lt;/span&gt;
&lt;span class="nv"&gt;branchpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/settings/branches
&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'url = '&lt;/span&gt; .git/config | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f2-&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'='&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^ *//g'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/\.git *$//g'&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if case&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;git@&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/:/\//g'&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^git@/https:\/\//g'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;git branch &lt;span class="nt"&gt;-m&lt;/span&gt; master main
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;span class="nb"&gt;echo &lt;/span&gt;Go update the default &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;branchpath&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;Then run:
&lt;span class="nb"&gt;echo &lt;/span&gt;git push origin &lt;span class="nt"&gt;--delete&lt;/span&gt; master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It doesn’t have any error checking, so don’t run it outside your repository’s local root folder, where you have administrative control over the remote/upstream repository.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;url&lt;/code&gt; variable digs into the git configuration file to find the &lt;code&gt;remote&lt;/code&gt; repository. If the &lt;code&gt;url&lt;/code&gt; we find starts with &lt;code&gt;git@&lt;/code&gt;, modify it to a real GitHub URL instead of GitHub’s &lt;code&gt;git@github.com:name/project.git&lt;/code&gt; format. All the pieces are there, of course. The &lt;code&gt;case&lt;/code&gt; syntax looks like a mess, but it works without any special exceptions to compare the names and should be completely portable across shell scripts.&lt;/p&gt;

&lt;p&gt;Then, we have two &lt;code&gt;git&lt;/code&gt; commands. The first moves (&lt;code&gt;-m&lt;/code&gt;) or renames the &lt;code&gt;master&lt;/code&gt; branch to &lt;code&gt;main&lt;/code&gt;. Next, we &lt;code&gt;push&lt;/code&gt; the new branch and set the upstream (&lt;code&gt;-u&lt;/code&gt;) to point to it. At this point, the local repository only has a &lt;code&gt;main&lt;/code&gt; branch and is set to work exclusively with the remote &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;Why &lt;code&gt;main&lt;/code&gt; and not &lt;code&gt;trunk&lt;/code&gt;, &lt;code&gt;primary&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;, or something else entirely? When I change branches (not that I have many public-facing branches), my muscle-memory is still for typing &lt;code&gt;git branch ma&lt;/code&gt; and then letting the shell handle auto-completion. You can call yours what you want, obviously, and even update the script to default to a name if you don’t provide it with one without too much trouble.&lt;/p&gt;

&lt;p&gt;However, there are still two manual steps remaining. Over on the GitHub page, the default branch still reads &lt;strong&gt;master&lt;/strong&gt;, so someone needs to change it by hand. So, that &lt;code&gt;url&lt;/code&gt; work the script did earlier pays off here, producing a link to &lt;code&gt;https://github.com/NAME/PROJECT/settings/branches&lt;/code&gt;, where the user can update the default. Obviously, I make no claims about the URL being correct for any host other than GitHub. (I really should start looking at GitLab; they seem like a nice company that people should pay some attention to.)&lt;/p&gt;

&lt;p&gt;Once that’s done, you can delete the &lt;code&gt;master&lt;/code&gt; branch entirely and never worry about it again…except for the rest of this post, because there are still some edge cases and improvements.&lt;/p&gt;

&lt;p&gt;I’m not currently running any CI/CD, but if you choose to work with this script and do, you should take a minute and make sure that the tool never explicitly references the &lt;code&gt;master&lt;/code&gt; branch in any of its work, at this point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced
&lt;/h3&gt;

&lt;p&gt;It eventually dawned on me that the big manual step (changing the default branch) should &lt;em&gt;probably&lt;/em&gt; be handled through GitHub’s API, so that the entire experience is just running a script. I initially didn’t feel like digging into it for &lt;em&gt;this&lt;/em&gt; post, because learning how to make and handle API calls takes this project from a “quick script” to a full application that needs to handle remote authentication and all the possible error states. So, I wasn’t going down that path…until I remembered that GitHub has &lt;a href="https://cli.github.com/"&gt;a CLI tool&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Installing that takes care of the “application” part of this. And with some research, I was able to work out how change the default branch &lt;em&gt;entirely&lt;/em&gt; through the tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;gh api repos/the-user/the-repository -X PATCH
  -F name="the-repository" \
  -F default_branch="main"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously, change &lt;code&gt;the-user&lt;/code&gt; and &lt;code&gt;the-repository&lt;/code&gt; (both instances) to yours. But now that we have this piece of information, we can replace the &lt;code&gt;echo&lt;/code&gt; lines above with the following.&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="nv"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f4&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f5&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
gh api repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;repo&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PATCH &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;repo&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nv"&gt;default_branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;
git push origin &lt;span class="nt"&gt;--delete&lt;/span&gt; master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We know that a repository URL is going to look like &lt;code&gt;https://github.com/the-user/the-repository/&lt;/code&gt;, so we pull off the fourth and fifth items, as delimited by slashes. The first is &lt;code&gt;https:&lt;/code&gt;, the second is empty, and so forth.&lt;/p&gt;

&lt;p&gt;Then, we call the GitHub CLI tool. The first time you successfully call it, it will ask you to hit Enter to open GitHub in a browser. There, you can authorize the tool to operate on your behalf. Then, once you’re authenticated, the script continues on to delete the branch.&lt;/p&gt;

&lt;p&gt;Except for the one-time authentication step, there are no manual steps. I call that a success.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Clones
&lt;/h2&gt;

&lt;p&gt;The above is all well and good, but it won’t work for an ordinary clone, because the work there is primarily administrative. If you have a clone of a project that has been making changes like this (for example, those of my projects that have migrated over), you’ll see an error something like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;From url-to-repository
 * [new branch] main -&amp;gt; origin/main
Your configuration specifies to merge with the ref 'refs/heads/master'
from the remote, but no such ref was fetched.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above script won’t work, because it wants to affect the origin, which has already been affected and you might not have access to. Instead, you need something more straightforward, like the following.&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;#!/bin/sh&lt;/span&gt;
git checkout master
git branch &lt;span class="nt"&gt;-m&lt;/span&gt; master main
git fetch
git branch &lt;span class="nt"&gt;--unset-upstream&lt;/span&gt;
git branch &lt;span class="nt"&gt;-u&lt;/span&gt; origin/main
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes sure that you start out on the &lt;code&gt;master&lt;/code&gt; branch (to make sure that you don’t break a different branch), moves/renames the branch as above, gets the latest commits from upstream, disconnects the upstream repository, links the new branch upstream (almost the same as above, though not quite), and finally updates the clone’s local default branch to point to the right place.&lt;/p&gt;

&lt;p&gt;Like above, run it in the clone’s root folder and don’t expect the script to recover easily if there are any errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Never Again…
&lt;/h2&gt;

&lt;p&gt;It would, of course, be nice if we never needed to see a &lt;code&gt;master&lt;/code&gt; branch again. Most of us don’t yet have the ability to just tell &lt;code&gt;git&lt;/code&gt; that we never want a repository with one, but we can create a decent alias to use instead of &lt;code&gt;git init&lt;/code&gt; until we do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;git config --global alias.new '!git init &amp;amp;&amp;amp; git symbolic-ref HEAD refs/heads/main'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can call &lt;code&gt;git new&lt;/code&gt; to initialize our repositories and the alias will create it and set the default branch name.&lt;/p&gt;

&lt;p&gt;That’s not an ideal situation, since muscle memory will inevitably type the default command that we can’t override. But as a temporary measure, it should work out for most cases until the latest version of &lt;code&gt;git&lt;/code&gt; shows up. To prepare for that, you might as well configure things now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;git config --global init.defaultBranch main
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Git 2.28 comes to your platform (Ubuntu is still on v2.25, for example, and if you're on a machine that doesn't auto-update git, you're probably always a few versions behind, too), it will pick up that option and all of your new local repositories will have default branches with your custom name.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is &lt;a href="https://pxhere.com/en/photo/997803"&gt;tree, nature, branch, winter, black and white, plant, trunk, bark, high, monochrome, twig, trees, twigs, woods, branches, tall, monochrome photography, atmospheric phenomenon, woody plant&lt;/a&gt; by an anonymous photographer, released under the terms of &lt;a href="https://creativecommons.org/publicdomain/zero/1.0/"&gt;CC0 1.0 Universal Public Domain Dedication&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>git</category>
      <category>automation</category>
    </item>
    <item>
      <title>Configuring a Visual Studio Code Extension</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Wed, 15 Jul 2020 11:51:02 +0000</pubDate>
      <link>https://dev.to/jcolag/configuring-a-visual-studio-code-extension-4lo9</link>
      <guid>https://dev.to/jcolag/configuring-a-visual-studio-code-extension-4lo9</guid>
      <description>&lt;p&gt;As a quick note, I &lt;a href="https://john.colagioia.net/blog/2020/07/15/vsconfig.html"&gt;released this post&lt;/a&gt; on my blog, this morning, so it can get to be (as I tend to be) a bit rambling.  Oh, and the original text is &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;on GitHub&lt;/a&gt; (licensed CC-BY-SA), so if anything seems muddy, by all means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave a comment here,&lt;/li&gt;
&lt;li&gt;Leave a comment on the blog,&lt;/li&gt;
&lt;li&gt;File an issue on GitHub, or&lt;/li&gt;
&lt;li&gt;Add a pull request!&lt;/li&gt;
&lt;/ul&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/jcolag"&gt;
        jcolag
      &lt;/a&gt; / &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;
        entropy-arbitrage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      John Colagioia's Blog Posts
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ktRp9n0G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4p48huhag8tawohywz6r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ktRp9n0G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/4p48huhag8tawohywz6r.png" alt="Visual Studio Codium" title="Visual Studio Codium"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Continuing &lt;a href="///blog/2020/07/08/vscode.html"&gt;the discussion&lt;/a&gt; on creating an extension for Visual Studio Code, and now that &lt;a href="https://github.com/jcolag/vscode-rat"&gt;&lt;strong&gt;VSCode Rat&lt;/strong&gt;&lt;/a&gt; is—seemingly against all odds—working &lt;em&gt;far&lt;/em&gt; better than it has any right to work, it’s time to add a configuration page. After all, it doesn’t make a lot of sense to ask people to blindly trust the URL that I provided or expect them to edit the code to insert their custom URL.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jcolag" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sapBLxTp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--tgHv7oJJ--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/349233/9aa9cddf-b6e0-46e9-84f9-4891041fcb5b.png" alt="jcolag image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jcolag/writing-visual-studio-code-extensions-42fi" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Writing Visual Studio Code Extensions&lt;/h2&gt;
      &lt;h3&gt;John Colagioia (he/him) ・ Jul  8 '20 ・ 5 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#techtips&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#vscode&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&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/jcolag"&gt;
        jcolag
      &lt;/a&gt; / &lt;a href="https://github.com/jcolag/vscode-rat"&gt;
        vscode-rat
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An extension (that nobody should use) to Visual Studio Code to "rat out" what the user is working on
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 

&lt;p&gt;Like last time, this turns out to be a project where it’s harder to find the information needed than to actually perform the tasks required. After a shocking amount of digging, I &lt;em&gt;finally&lt;/em&gt; found the poorly named &lt;a href="https://code.visualstudio.com/api/references/contribution-points"&gt;Contribution Points&lt;/a&gt; reference, which gives an overview of…a lot.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding Configuration Options
&lt;/h2&gt;

&lt;p&gt;In the process of creating the extension, you might recall the &lt;code&gt;contributes&lt;/code&gt; property in the &lt;code&gt;package.json&lt;/code&gt; file. We used it to add a &lt;em&gt;command&lt;/em&gt; to explicitly call our new feature.&lt;/p&gt;

&lt;p&gt;That command is now obsolete, since all the &lt;strong&gt;VSCode Rat&lt;/strong&gt; code runs autonomously, but the property is still useful, because it’s where our configuration options go. In the case of this extension, the property looks something like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"contributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commands"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vscode-rat.helloWorld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hello World"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VSCode Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"vscodeRat.targetUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Server waiting for VSCode Rat to rat you out!"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We only have one option: &lt;code&gt;vscodeRat.targetUrl&lt;/code&gt;, which takes a string (the URL), and has a default and description. And we can see that instantly, the next time we run, by opening &lt;em&gt;File&lt;/em&gt;/&lt;em&gt;Preferences&lt;/em&gt;/&lt;em&gt;Settings&lt;/em&gt; in the menu, opening the &lt;em&gt;Extensions&lt;/em&gt; item, and scrolling to the bottom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y-aEGsE3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tgeiuff4i2naz2016akg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y-aEGsE3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tgeiuff4i2naz2016akg.png" alt="A Configuration Option" title="A configuration option!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That, then, gives us our option. If we need more, we can add more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not Quite
&lt;/h3&gt;

&lt;p&gt;Now that I think about it, we probably want that URL to actually &lt;em&gt;be&lt;/em&gt; a URL, so we need some validation. Visual Studio Code doesn’t give us &lt;em&gt;great&lt;/em&gt; validation, but it’s decent. We can set a &lt;code&gt;pattern&lt;/code&gt; property on each configuration option, which is a regular expression. So, we’ll throw that in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"vscodeRat.targetUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Server waiting for VSCode Rat to rat you out!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https?:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;/(www&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.)?[-a-zA-Z0-9@:%._&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;+~#=]{1,256}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.[a-zA-Z0-9()]{1,6}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;b([-a-zA-Z0-9()@:%_&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;+.~#?&amp;amp;//=]*)"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since JSON doesn’t know or care about regular expressions, all the back-slashes in the regular expression need to be escaped…with back-slashes, which is why they’re doubled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Configuration Options
&lt;/h2&gt;

&lt;p&gt;Now that we have a URL for any server willing to accept our POSTs, we can also use it. Since the configuration options can change at any time, we need to check it on every use, modifying our handler code to something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onDidOpenTextDocument&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;document&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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileName&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;post_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Opened &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vscodeRat&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;urlParts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&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;targetUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;httpPost&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;urlParts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;urlParts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll notice that we now call &lt;code&gt;vscode.workspace.getConfiguration()&lt;/code&gt; with the prefix of our configuration options specified above, to get an object with the extension’s options. It returns something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;targetUrl:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, I’m using Node’s built-in &lt;code&gt;url&lt;/code&gt; library to parse the single URL to provide the host name and path required by the &lt;code&gt;httpPost()&lt;/code&gt; function I snagged for last week.&lt;/p&gt;

&lt;p&gt;And that’s it. I can now configure &lt;strong&gt;VSCode Rat&lt;/strong&gt; to send all of its updates to a mailbox on &lt;a href="http://ptsv2.com/"&gt;Henry’s Post Test Server&lt;/a&gt;, and we all lived happily ever after. Well, almost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Oops…
&lt;/h2&gt;

&lt;p&gt;When creating the extension, in order to get things moving as quickly as possible, I prototyped with Node’s built-in &lt;code&gt;http&lt;/code&gt; library, which shouldn’t make HTTPS calls. That’s fine for the sake of this experiment, but an extension ready for production will need to handle &lt;em&gt;any&lt;/em&gt; kind of URL, which probably means choosing a different networking library—I’ve heard good things about &lt;a href="https://www.npmjs.com/package/axios"&gt;axios&lt;/a&gt;, for example—and rewriting the POST calls to work with it.&lt;/p&gt;

&lt;p&gt;That, however, is far less important. At this point, we now have an extension that we can point to any server, even if the code itself isn't prepared to carry out that promise.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is the sample image for &lt;a href="https://vscodium.com/"&gt;VSCodium&lt;/a&gt; and made available (like the application and their entire site) under the terms of the MIT License.&lt;/p&gt;

</description>
      <category>techtips</category>
      <category>programming</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Writing Visual Studio Code Extensions</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Wed, 08 Jul 2020 11:15:02 +0000</pubDate>
      <link>https://dev.to/jcolag/writing-visual-studio-code-extensions-42fi</link>
      <guid>https://dev.to/jcolag/writing-visual-studio-code-extensions-42fi</guid>
      <description>&lt;p&gt;As a quick note, I &lt;a href="https://john.colagioia.net/blog/2020/07/08/vscode.html"&gt;released this post&lt;/a&gt; on my blog, this morning, so it can get to be (as I tend to be) a bit rambling.  Oh, and the original text is &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;on GitHub&lt;/a&gt; (licensed CC-BY-SA), so if anything seems muddy, by all means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave a comment here,&lt;/li&gt;
&lt;li&gt;Leave a comment on the blog,&lt;/li&gt;
&lt;li&gt;File an issue on GitHub, or&lt;/li&gt;
&lt;li&gt;Add a pull request!&lt;/li&gt;
&lt;/ul&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/jcolag"&gt;
        jcolag
      &lt;/a&gt; / &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;
        entropy-arbitrage
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      John Colagioia's Blog Posts
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mnr-wJW0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2ctffo2kckwpycwwo133.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mnr-wJW0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2ctffo2kckwpycwwo133.png" alt="VSCodium" title="VSCodium"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to the work on creating a browser extension such as &lt;a href="https://github.com/jcolag/url-rat"&gt;&lt;strong&gt;URL Rat&lt;/strong&gt;&lt;/a&gt;—&lt;a href="///blog/2020/06/17/plugin.html"&gt;part 1&lt;/a&gt; and &lt;a href="///blog/2020/06/24/store.html"&gt;part 2&lt;/a&gt; for what it took to make those work...&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/jcolag"&gt;
        jcolag
      &lt;/a&gt; / &lt;a href="https://github.com/jcolag/url-rat"&gt;
        url-rat
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A browser extension to send visited URLs to a server, written as a test
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 


&lt;div class="ltag__link"&gt;
  &lt;a href="/jcolag" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sapBLxTp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--tgHv7oJJ--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/349233/9aa9cddf-b6e0-46e9-84f9-4891041fcb5b.png" alt="jcolag image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jcolag/writing-browser-extensions-4gkh" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Writing Browser Extensions&lt;/h2&gt;
      &lt;h3&gt;John Colagioia (he/him) ・ Jun 18 '20 ・ 5 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#browser&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#extension&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="/jcolag" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sapBLxTp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--tgHv7oJJ--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/349233/9aa9cddf-b6e0-46e9-84f9-4891041fcb5b.png" alt="jcolag image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jcolag/writing-browser-extensions-with-configuration-4gm4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Writing Browser Extensions with Configuration&lt;/h2&gt;
      &lt;h3&gt;John Colagioia (he/him) ・ Jun 26 '20 ・ 5 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#techtips&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programming&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#browser&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I’ve been looking at what it takes to develop an extension for Visual Studio Code, &lt;a href="https://github.com/jcolag/vscode-rat"&gt;&lt;strong&gt;VSCode Rat&lt;/strong&gt;&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/jcolag"&gt;
        jcolag
      &lt;/a&gt; / &lt;a href="https://github.com/jcolag/vscode-rat"&gt;
        vscode-rat
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An extension (that nobody should use) to Visual Studio Code to "rat out" what the user is working on
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;
 
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;The easy way to get moving is to use the &lt;a href="https://yeoman.io/"&gt;Yeoman&lt;/a&gt; scaffolding tool to generate the base code for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--global&lt;/span&gt; yo generator-code
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;yo code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The yeoman (that’s the ASCII art fellow) will then ask for some basic information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The kind of extension: A normal extension, color theme, language, snippet, key map, or pack of extensions;&lt;/li&gt;
&lt;li&gt;The extension’s name;&lt;/li&gt;
&lt;li&gt;The extension’s ID;&lt;/li&gt;
&lt;li&gt;The extension’s description;&lt;/li&gt;
&lt;li&gt;Whether you want type-checking in the configuration file;&lt;/li&gt;
&lt;li&gt;Whether you need a git repository; and&lt;/li&gt;
&lt;li&gt;Your preferred package manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This list of questions might change by the time you read this, but it should give you an idea of what to expect, regardless.&lt;/p&gt;

&lt;p&gt;The file that matters most, of course, is &lt;code&gt;extension.js&lt;/code&gt;. There’s also &lt;code&gt;jsconfig.json&lt;/code&gt;, which you can look at and—at least for now—confirm that it’s mostly just the answers you gave to the questions.&lt;/p&gt;
&lt;h3&gt;
  
  
  Packaging
&lt;/h3&gt;

&lt;p&gt;If you’re not familiar with Node.js projects, you’ll also want to get familiar with &lt;code&gt;package.json&lt;/code&gt;. You rarely need to update it manually, but it’s good to know what’s lurking in there. Whether you know this file or not, the new aspects are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;activationEvents&lt;/code&gt;: The term is not subtle, referring to the list of events that activate the extension’s code. Visual Studio Code won’t load the extension until one of those events occurs. The default is a custom “Hello, World” event.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commands&lt;/code&gt;: Here, you’ll find a list of commands the user can run to operate the extension. The default—probably no surprise, given the custom event’s name—is a “Hello, World!” command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can probably guess that those are going to be important.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;The easy way to test the extension is to open the folder in Visual Studio Code for editing and use the existing scripts to launch a new instance of Code with your extension loaded. To do that, click the “Run” button on the left (#1 on the picture below)—you can also type Ctrl+Shift+D—to get to the run/debug commands, then click the green &lt;em&gt;Start Debugging&lt;/em&gt; arrow, marked #2 in the image.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wbdVvfr0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vs4ah1glrp7n4rtf0a6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wbdVvfr0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vs4ah1glrp7n4rtf0a6a.png" alt="Run VSCode with the extension" title="Run VSCode with the extension"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will start a second Visual Studio Code window. From here, you can open the Command Palette (&lt;em&gt;View&lt;/em&gt;/&lt;em&gt;Command Palette…&lt;/em&gt; or Ctrl+Shift+P) and, as you start typing &lt;code&gt;Hello&lt;/code&gt;, you’ll see the new “Hello World” command listed. Select it and you’ll get a little toast notification, fulfilling the extension’s registered action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HGQ7EYlC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2rla410892b64x8eh2lm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HGQ7EYlC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2rla410892b64x8eh2lm.png" alt="Hello, back" title="Hello, back"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, that’s what we get without doing &lt;em&gt;any&lt;/em&gt; work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Monitoring Files
&lt;/h2&gt;

&lt;p&gt;So, now we need to fill in real code.&lt;/p&gt;

&lt;p&gt;The first step is going to be to give our extension a reason to run &lt;em&gt;other&lt;/em&gt; than a manual command. Microsoft has provided a &lt;a href="https://code.visualstudio.com/api/references/activation-events"&gt;list of activation events&lt;/a&gt;, but as mentioned, that tells Visual Studio Code when to &lt;em&gt;load&lt;/em&gt; the extension, not when to &lt;em&gt;run&lt;/em&gt; the extension.&lt;/p&gt;

&lt;p&gt;In our case, since we want to monitor what our user is doing—just like with &lt;strong&gt;URL Rat&lt;/strong&gt;, &lt;em&gt;please&lt;/em&gt; don’t try to use this in production, because it’s a security nightmare—we want to load the extension when the editor starts, so that just looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"activationEvents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As in, change the &lt;code&gt;activationEvents&lt;/code&gt; list in &lt;code&gt;package.json&lt;/code&gt; to that.&lt;/p&gt;

&lt;p&gt;Then, take a look at &lt;code&gt;extension.js&lt;/code&gt;. The only code that looks important is &lt;code&gt;activate()&lt;/code&gt;, which registers and subscribes to the command. So, we need to make our changes, there, subscribing to events that are relevant. Try something like the following in that &lt;code&gt;activate()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onDidSaveTextDocument&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;document&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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showInformationMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Saved &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&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="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onDidOpenTextDocument&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;document&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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showInformationMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Opened &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&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="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onDidChangeTextDocument&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;changes&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;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showInformationMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Modified &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is extremely primitive, but it points us in the right direction. Rather than pop up the static message when the user asks for it, we now pop up messages when files are opened, saved, and modified.&lt;/p&gt;
&lt;h2&gt;
  
  
  Sending to a Server
&lt;/h2&gt;

&lt;p&gt;Unfortunately, the version of JavaScript supported by Visual Studio Code doesn’t &lt;em&gt;appear&lt;/em&gt; to support &lt;code&gt;fetch()&lt;/code&gt;, so we can’t pull off the same trick we did with &lt;strong&gt;URL Rat&lt;/strong&gt;. Instead, we need a function that looks something like &lt;a href="https://stackoverflow.com/a/50891354/3438854"&gt;this one&lt;/a&gt;, conveniently licensed CC-BY-SA 4.0, like the rest of my blog.&lt;/p&gt;


&lt;div class="ltag__stackexchange--container"&gt;
  &lt;div class="ltag__stackexchange--title-container"&gt;
    
      &lt;div class="ltag__stackexchange--title"&gt;
        &lt;h1&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Gn-iPj_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackoverflow-logo-b42691ae545e4810b105ee957979a853a696085e67e43ee14c5699cf3e890fb4.svg" alt=""&gt;
            &lt;a href="https://stackoverflow.com/questions/6158933/how-is-an-http-post-request-made-in-node-js/50891354#50891354" rel="noopener noreferrer"&gt;
              &lt;span class="title-flare"&gt;answer&lt;/span&gt; re:  How is an HTTP POST request made in node.js?
            &lt;/a&gt;
        &lt;/h1&gt;
        &lt;div class="ltag__stackexchange--post-metadata"&gt;
          &lt;span&gt;Jun 16 '18&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;a class="ltag__stackexchange--score-container" href="https://stackoverflow.com/questions/6158933/how-is-an-http-post-request-made-in-node-js/50891354#50891354" rel="noopener noreferrer"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9mJpuJP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackexchange-arrow-up-eff2e2849e67d156181d258e38802c0b57fa011f74164a7f97675ca3b6ab756b.svg" alt=""&gt;
        &lt;div class="ltag__stackexchange--score-number"&gt;
          48
        &lt;/div&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wif5Zq3z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/stackexchange-arrow-down-4349fac0dd932d284fab7e4dd9846f19a3710558efde0d2dfd05897f3eeb9aba.svg" alt=""&gt;
      &lt;/a&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--body"&gt;
    
&lt;p&gt;Simple and dependency-free. Uses a Promise so that you can await the result. It returns the response body and does not check the response status code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const https = require('https')
function httpsPost({body, ...options}) {
    return new Promise((resolve,reject) =&amp;gt; {
        const req = https.request({
            method: 'POST',
            ...options,
        }, res =&amp;gt; {&lt;/code&gt;&lt;/pre&gt;…
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--btn--container"&gt;
    
      &lt;a href="https://stackoverflow.com/questions/6158933/how-is-an-http-post-request-made-in-node-js/50891354#50891354" rel="noopener noreferrer"&gt;Open Full Answer&lt;/a&gt;
    
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;httpPost&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="nx"&gt;options&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="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;reject&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;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
          &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;chunks&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;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-type&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;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                      &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&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="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="nx"&gt;resolve&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="p"&gt;})&lt;/span&gt;
      &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can replace our &lt;code&gt;vscode.window.showInformationMessage()&lt;/code&gt; function calls with something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;httpPost&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Opened &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileName&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="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verb used in the string depends on which action we’re talking about, of course. In addition, we might want to do something more with the change actions. When the files change, the object contains the same filename information as the other actions, but also includes enough information to describe the change, including the replacement text, the file offset where the change occurs, and the start and end line and column of the change.&lt;/p&gt;

&lt;p&gt;With that information, we can collect the incremental changes made in Visual Studio Code over time, which would make it easy to reconstruct ideas that were either deleted and forgotten or lost in a crash.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Left?
&lt;/h2&gt;

&lt;p&gt;Like with the “first draft” of &lt;strong&gt;URL Rat&lt;/strong&gt; , you probably noticed that &lt;strong&gt;VSCode Rat&lt;/strong&gt; uses a hard-coded URL, whereas we would obviously want to be able to configure that information, just like we would with any other extension. And just like with the browser extension posts, this post is getting long enough that it’s probably a better idea to save that configuration discussion for next week.&lt;/p&gt;

&lt;p&gt;Otherwise, it seems surprisingly difficult to know what events we can subscribe to and what they do (it took a long time to track down &lt;code&gt;vscode.workspace.onDidSaveTextDocument()&lt;/code&gt;, probably longer than it should have), but once you know them, the rest of the development is remarkably fast.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is the sample image for &lt;a href="https://vscodium.com/"&gt;VSCodium&lt;/a&gt; and made available (like the application and their entire site) under the terms of the MIT License.&lt;/p&gt;

</description>
      <category>techtips</category>
      <category>programming</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Writing Browser Extensions with Configuration</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Fri, 26 Jun 2020 11:33:02 +0000</pubDate>
      <link>https://dev.to/jcolag/writing-browser-extensions-with-configuration-4gm4</link>
      <guid>https://dev.to/jcolag/writing-browser-extensions-with-configuration-4gm4</guid>
      <description>&lt;p&gt;As a quick note, I &lt;a href="https://john.colagioia.net/blog/2020/06/24/store.html" rel="noopener noreferrer"&gt;released this post&lt;/a&gt; on my blog yesterday, so it can get to be (as I tend to be) a bit rambling.  Oh, and the original text is &lt;a href="https://github.com/jcolag/entropy-arbitrage" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt; (licensed CC-BY-SA), so if anything seems muddy, by all means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave a comment here,&lt;/li&gt;
&lt;li&gt;Leave a comment on the blog,&lt;/li&gt;
&lt;li&gt;File an issue on GitHub, or&lt;/li&gt;
&lt;li&gt;Add a pull request!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This follows on to &lt;a href="https://dev.to/jcolag/writing-browser-extensions-4gkh"&gt;my post from last week&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/jcolag" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuploads%2Fuser%2Fprofile_image%2F349233%2F9aa9cddf-b6e0-46e9-84f9-4891041fcb5b.png" alt="jcolag"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/jcolag/writing-browser-extensions-4gkh" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Writing Browser Extensions&lt;/h2&gt;
      &lt;h3&gt;John Colagioia (he/him) ・ Jun 18 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#browser&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#extension&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;To recap it briefly, I have recently been investigating some possible projects that would benefit from having a simple browser extension to pass along real-time data on the user’s actions. It’s simple enough, but has enough detail to make a viable post…or two.&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%2Fh1ssymqlthsti06ibt7w.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%2Fh1ssymqlthsti06ibt7w.png" title="Plugs" alt="Plugs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, our extension is going to report every visited URL to a configurable remote address. The &lt;a href="https://github.com/jcolag/url-rat" rel="noopener noreferrer"&gt;URL Rat&lt;/a&gt; extension, by last week’s post, was able to record every visited URL and send that information to a hard-coded URL. So far, so good. I’ll assume that you read that, since it’s short.&lt;/p&gt;

&lt;p&gt;But now, we need to make that user-configurable, so we need to work with the browser storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Configuration
&lt;/h2&gt;

&lt;p&gt;First, we need to set up the extension to allow for some configuration work, which means adding some elements to &lt;code&gt;manifest.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"options_ui"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"page"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popup/configure.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser_style"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"browser_action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"default_icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/urlrat32.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"default_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"URL Rat"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"background.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of the three does its small part.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Action
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;browser_action&lt;/code&gt; element creates a toolbar button, in this case with an additional image that fits the button profile. All things considered, it’s pretty boring. Name and image.&lt;/p&gt;

&lt;p&gt;The handlers go in…&lt;/p&gt;

&lt;h3&gt;
  
  
  Background
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;background.scripts&lt;/code&gt; element, we list the files containing the toolbar button’s event handlers. In this case, we just have one file, since we’re only handling one click. But if we had multiple buttons and/or had multiple features, we might consider separating that code into multiple files, listing them there.&lt;/p&gt;

&lt;p&gt;I’ll talk about our click-handler in a bit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration Interface
&lt;/h3&gt;

&lt;p&gt;Finally, the &lt;code&gt;options_ui&lt;/code&gt; element tells the browser where to find the page with the configuration controls. In my case, I created a &lt;code&gt;popup&lt;/code&gt; folder—even though it doesn’t actually pop up, but I initially considered that approach and never changed the name—where I’ve dumped all the code related to that options page.&lt;/p&gt;

&lt;p&gt;These three items guide most of the work from here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event Handlers
&lt;/h2&gt;

&lt;p&gt;As mentioned, we keep the toolbar handlers in a script that the browser runs in the background. In our case, we don’t have much to do, so it’s just this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openOptionsPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browserAction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just…open the options page when we hear that the button has been clicked.&lt;/p&gt;

&lt;p&gt;Honestly, we don’t even need this, since the options page is going to be accessible from the list of browser extensions, but we might want to add features later and I wanted to make the extension visible, since it’s such a terrible idea to run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;I’ll spare you the boring HTML and CSS for &lt;strong&gt;URL Rat&lt;/strong&gt; ’s options page. It’s a form with controls. If you’re not familiar with how those work, I’m not being dismissive when I say that you can find a better explanation than I could write here in just about any HTML tutorial.&lt;/p&gt;

&lt;p&gt;Instead, we’ll just focus on the JavaScript code, since that’s the part that interacts with the browser storage. There are a few pieces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveOptions&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#dest&lt;/span&gt;&lt;span class="dl"&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="na"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#on&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;saveOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save options will take our two options (a URL and an on/off setting) and push them to &lt;code&gt;browser.storage.sync&lt;/code&gt;, where we can pick them up later. I slightly reorganized the file for clarity, but the call happens when our options form is submitted.&lt;/p&gt;

&lt;p&gt;In other words, the user clicks “Save” and the settings are stored in the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;restoreOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;storageItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed&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;storageItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;gettingItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&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;gettingItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;restoreOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we open the options page, we want the controls to reflect the saved data. So, &lt;code&gt;restoreOptions()&lt;/code&gt; takes &lt;em&gt;two&lt;/em&gt; steps, instead of just the one involved in saving.&lt;/p&gt;

&lt;p&gt;The first step is to pull the default options out of the &lt;code&gt;browser.storage.managed&lt;/code&gt; area. After pushing that information to the options page, we check &lt;code&gt;browser.storage.sync&lt;/code&gt; to see if the user has saved anything and, if so, set those options and overwrite the managed version.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;setOptions()&lt;/code&gt; function isn’t worth showing here, just a utility function to avoid duplicating some updates to the controls. You can find it in the repository along with the HTML and CSS, if you need it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wait, What Default Values?
&lt;/h3&gt;

&lt;p&gt;You noticed that there’s no way to populate the manage storage, too, right? Getting this to work was probably the least-appealing part of the process for me.&lt;/p&gt;

&lt;p&gt;This may only be for Firefox, but we need a JSON file to get information there. You might remember that the &lt;code&gt;manifest.json&lt;/code&gt; file included a &lt;code&gt;browser_specific_settings.gecko.id&lt;/code&gt; element. Implausible as it sounds, we use that ID to identify a new JSON file that holds our default values. In this case, it’s literally named &lt;code&gt;urlrat@example.com.json&lt;/code&gt;, because our identifier is that imaginary e-mail address. And it looks like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urlrat@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ignored"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"storage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isActive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy, link, or move that file—depending on your preferences and how often you expect to update and keep the file around—to your &lt;code&gt;~/.mozilla/managed-storage/&lt;/code&gt; folder, and you have now initialized your browser’s Managed Storage.&lt;/p&gt;

&lt;p&gt;I &lt;em&gt;did&lt;/em&gt; warn you that it wasn’t appealing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the Configuration
&lt;/h2&gt;

&lt;p&gt;From here, we should already know what to do. The main extension code, &lt;code&gt;url-rat.js&lt;/code&gt; for this example, now needs to replace its hard-coded values with values from the browser’s storage. I took the following approach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;managed&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;local&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;var&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;managed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Assign the values in the "store" variable to individual&lt;/span&gt;
        &lt;span class="c1"&gt;// variables already used by the extension, and then put the&lt;/span&gt;
        &lt;span class="c1"&gt;// original extension code 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;I nested the &lt;code&gt;then()&lt;/code&gt; handlers to make sure we have the data, even though that’s probably unnecessary, and then used &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign" rel="noopener noreferrer"&gt;&lt;code&gt;Object.assign()&lt;/code&gt;&lt;/a&gt; to merge the two configuration objects into one.&lt;/p&gt;

&lt;p&gt;Like the comment says, I should now have &lt;code&gt;store.dest&lt;/code&gt; with the saved URL and &lt;code&gt;store.isActive&lt;/code&gt; to decide whether to send the current URL to the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overall
&lt;/h2&gt;

&lt;p&gt;All things considered, if we ignore the managed storage shenanigans, the development process seems smooth. The only real snag I hit was in not realizing that the &lt;code&gt;sync&lt;/code&gt; and &lt;code&gt;managed&lt;/code&gt; storage areas were different, breaking the configuration page.&lt;/p&gt;

&lt;p&gt;Also, the configuration page is ugly.&lt;/p&gt;

&lt;p&gt;Other than minor issues like that, though, the fact that it only took a couple of hours in total to hack out a fully functional browser extension that performs a defined task starts to make this look appealing for certain kinds of projects.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is &lt;a href="https://pxhere.com/en/photo/779821" rel="noopener noreferrer"&gt;Untitled&lt;/a&gt; by an anonymous PxHere user, made available under the terms of the &lt;a href="https://creativecommons.org/publicdomain/zero/1.0/" rel="noopener noreferrer"&gt;CC0 1.0 Universal Public Domain Dedication&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>techtips</category>
      <category>programming</category>
      <category>javascript</category>
      <category>browser</category>
    </item>
    <item>
      <title>Writing Browser Extensions</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Wed, 17 Jun 2020 11:11:02 +0000</pubDate>
      <link>https://dev.to/jcolag/writing-browser-extensions-4gkh</link>
      <guid>https://dev.to/jcolag/writing-browser-extensions-4gkh</guid>
      <description>&lt;p&gt;As a quick note, I &lt;a href="https://john.colagioia.net/blog/2020/06/17/plugin.html"&gt;released this post&lt;/a&gt; on my blog yesterday, so it can get to be (as I tend to be) a bit rambling.  Oh, and the original text is &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;on GitHub&lt;/a&gt; (licensed CC-BY-SA), so if anything seems muddy, by all means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave a comment here,&lt;/li&gt;
&lt;li&gt;Leave a comment on the blog,&lt;/li&gt;
&lt;li&gt;File an issue on GitHub, or&lt;/li&gt;
&lt;li&gt;Add a pull request!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I have recently been investigating some possible projects that would benefit from having a simple browser extension to pass along real-time data on the user’s actions. It’s simple enough, but has enough detail to make a viable post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ux22rbTN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5ky4e6he6dse9h1lexy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ux22rbTN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5ky4e6he6dse9h1lexy0.png" alt="Plugs" title="Plugs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, our extension is going to report every visited URL to a configurable remote address.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Short Version
&lt;/h2&gt;

&lt;p&gt;A browser extension for both Firefox and Chrome-based web browsers is some JavaScript code with a Manifest file. If you’re not packaging them up for the official download sites and are familiar with JavaScript, you can look up the manifest and work from there.&lt;/p&gt;

&lt;p&gt;It’s a &lt;em&gt;little&lt;/em&gt; bit more complicated than that, but not a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Layout
&lt;/h2&gt;

&lt;p&gt;A simple browser extension project has four parts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;manifest.json&lt;/code&gt;, which is (unsurprisingly) the project’s manifest file,&lt;/li&gt;
&lt;li&gt;Some JavaScript code that does what the extension needs,&lt;/li&gt;
&lt;li&gt;A folder for any assets that might be used, and&lt;/li&gt;
&lt;li&gt;Icons to represent the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the case of &lt;a href="https://github.com/jcolag/url-rat"&gt;URL Rat&lt;/a&gt;, it looks something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── icons
│   ├── border-48.png
│   └── border-96.png
├── LICENSE
├── manifest.json
├── README.md
└── url-rat.js

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;LICENSE&lt;/code&gt; and &lt;code&gt;README.md&lt;/code&gt; were created when I started the repository, and I created the images with &lt;a href="https://imagemagick.org/index.php"&gt;ImageMagick&lt;/a&gt;, based on the suggestions on &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension"&gt;Mozilla’s tutorial&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;convert -size 48x48 xc:#&lt;/span&gt;6187db border-48.png
&lt;span class="gp"&gt;convert -size 96x96 xc:#&lt;/span&gt;6187db border-96.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or whatever color I actually used. It’s not in my command history, for some reason. You can create a real icon, if that interests you for the purpose of the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manifest Destiny
&lt;/h2&gt;

&lt;p&gt;Because my plug-in needs to actually do something, I made some changes to the sample suggested by the Mozilla tutorial linked above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"URL Rat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sends each visited URL to a local server."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;all_urls&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"48"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/border-48.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"96"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"icons/border-96.png"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"browser_specific_settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gecko"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urlrat@example.com"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"matches"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"js"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"url-rat.js"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously, I changed the name, description, and script name. If this ever becomes a real project, the ID will need to change. But the two important items to talk about are the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;matches&lt;/code&gt; provides a list of patterns that a visited URL needs to match. In the case of the Mozilla example, it’s only for Mozilla pages, whereas mine will be active on all pages, hence &lt;code&gt;*://*/*&lt;/code&gt;, all protocols (HTTP, HTTPS, FTP, FTPS, and whatever else modern browsers support), all hosts, and all files on that host.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;permissions&lt;/code&gt; is the list of resources the extension needs to access. I &lt;em&gt;hate&lt;/em&gt; that this needs to be &lt;code&gt;&amp;lt;all urls&amp;gt;&lt;/code&gt;, allowing it to send data to and receive data from any page on the Internet, since that’s a potential security problem that a bad actor or clumsy developer could exploit. However, since we’ll eventually want to configure the destination URL to point to any server (not in this post), it does make sense to request that flexibility, regardless.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tried to narrow permission to a specific URL, like the one actually used in the HTTP requests, but couldn’t get that to work unless I specifically visited my own server, which is…somewhat less than useful.&lt;/p&gt;

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

&lt;p&gt;The code to capture and send every visited URL is simple enough, if a bit obnoxious to deal with the asynchronous code.&lt;/p&gt;

&lt;p&gt;The first line is just configuration. You’ll need your own server listening on a port, somewhere, and &lt;code&gt;url&lt;/code&gt; should point there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8080/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gets us our current URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;currentUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The aforementioned obnoxiousness. We create an asynchronous, anonymous function to call &lt;code&gt;fetch&lt;/code&gt;, so that we can call it immediately and not get yelled at by the interpreter for using &lt;code&gt;await&lt;/code&gt; inside something other than an asynchronous function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we make the request. Note that it’s an HTTP &lt;code&gt;POST&lt;/code&gt; request, so that it can usefully carry a message body (with the URL as that body), &lt;em&gt;but&lt;/em&gt; the server I threw together wasn’t recognizing the bodies, so I also stuffed it into the header as &lt;code&gt;X-This-Is-The-Url&lt;/code&gt;. The HTTP specification has no problem with you adding headers, as long as they all start with &lt;code&gt;X-&lt;/code&gt; to avoid confusing any parsing code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawResponse&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-This-Is-The-Url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUrl&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUrl&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we just need to wait for the response to come back and (if desired) do something with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&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;rawResponse&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="nx"&gt;content&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;Once debugging is complete, we can scrap the logging statement entirely, since it only clutters the console window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Extension
&lt;/h2&gt;

&lt;p&gt;For Firefox, the Mozilla tutorial is right on.  But to summarize...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;about:debugging&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;This Firefox&lt;/strong&gt; on the left-hand panel,&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Load Temporary Add-on&lt;/strong&gt; ,&lt;/li&gt;
&lt;li&gt;Navigate to your extension’s folder,&lt;/li&gt;
&lt;li&gt;Select any file in that folder, such as &lt;code&gt;manifest.json&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Open&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Assuming there were no errors, it should run until you reload or unload it, or until you shut Firefox down.&lt;/p&gt;

&lt;p&gt;On Chrome (or Chromium, and &lt;em&gt;probably&lt;/em&gt; most browsers built on Chromium, but I’m not testing them…), it’s similar.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;code&gt;chrome://extensions/&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Switch to &lt;strong&gt;Developer Mode&lt;/strong&gt; to the upper-right,&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Load Unpacked&lt;/strong&gt; to the upper-left,&lt;/li&gt;
&lt;li&gt;Navigate to your extension’s folder,&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Open&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chromium will complain about the &lt;code&gt;gecko.id&lt;/code&gt; field in the manifest, but that won’t affect your testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Next?
&lt;/h2&gt;

&lt;p&gt;This is already getting too long for a “tip,” so I’ll save it for next week, but the obvious next big step to make this usable would be to add a configuration pop-up to replace the current destination URL with something other than &lt;code&gt;http://localhost:8080&lt;/code&gt;. If you want to get to it before I do, the Mozilla tutorial links to “a more complex extension” that includes a toolbar button and a pop-up window. The &lt;a href="https://github.com/mdn/webextensions-examples/tree/master/favourite-colour"&gt;Favourite Coluor&lt;/a&gt; is closer to the idea of a configuration page, too.&lt;/p&gt;

&lt;p&gt;This would basically be that, but with a single place to fill in a URL (maybe validate it) and, optionally, a switch to turn the feature on and off to allow people to step out of the &lt;a href="https://en.wikipedia.org/wiki/Panopticon"&gt;Panopticon&lt;/a&gt; when necessary.&lt;/p&gt;

&lt;p&gt;Check back in next week, for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packaging
&lt;/h2&gt;

&lt;p&gt;Browser extensions are ZIP files of the folder contents (not the folder itself), renamed to &lt;code&gt;*.xpi&lt;/code&gt; for Firefox. Here’s &lt;a href="https://extensionworkshop.com/documentation/publish/package-your-extension/"&gt;Mozilla’s example&lt;/a&gt;. It can then be sent to whoever needs to sign it, and you have yourself a browser extension.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is &lt;a href="https://commons.wikimedia.org/wiki/File:Reisestecker.jpg"&gt;Fotowerkstatt&lt;/a&gt; by &lt;a href="https://commons.wikimedia.org/wiki/User:Mattes"&gt;Mattes&lt;/a&gt;, released into the public domain.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>browser</category>
      <category>extension</category>
    </item>
    <item>
      <title>Experimenting with Worker Threads</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Fri, 17 Apr 2020 11:17:21 +0000</pubDate>
      <link>https://dev.to/jcolag/experimenting-with-worker-threads-i7g</link>
      <guid>https://dev.to/jcolag/experimenting-with-worker-threads-i7g</guid>
      <description>&lt;p&gt;As a quick note, I &lt;a href="https://john.colagioia.net/blog/2020/04/15/worker.html" rel="noopener noreferrer"&gt;released&lt;/a&gt; this on my blog the other day and so it can get to be (as I tend to be) a bit rambling.  One big change is that the blog version has an additional section at the end with a bunch of non-color design resources that I recommend.  Oh, and the original text is &lt;a href="https://github.com/jcolag/entropy-arbitrage" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt; (licensed CC-BY-SA), so if anything seems muddy, by all means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Leave a comment here,&lt;/li&gt;
&lt;li&gt;Leave a comment on the blog,&lt;/li&gt;
&lt;li&gt;File an issue on GitHub, or&lt;/li&gt;
&lt;li&gt;Add a pull request!&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;As I have started working on &lt;a href="https://github.com/jcolag/Uxuyu" rel="noopener noreferrer"&gt;a prototype desktop client for the twtxt social network&lt;/a&gt;, one of the key technical aspects is making a large number of web requests. Since I’m prototyping this using &lt;a href="https://proton-native.js.org/#/" rel="noopener noreferrer"&gt;Proton Native&lt;/a&gt; and JavaScript is traditionally single-threaded, this presents a small problem:  Since web requests can take a while to complete, traditional programming techniques would lock up the user interface, and that isn’t really viable.&lt;/p&gt;

&lt;p&gt;Fortunately, as of &lt;a href="https://nodejs.org/en/blog/release/v10.5.0/" rel="noopener noreferrer"&gt;Node.js v10.5.0&lt;/a&gt;, JavaScript on the desktop (such as Proton Native) has what they call &lt;a href="https://nodejs.org/api/worker_threads.html" rel="noopener noreferrer"&gt;worker threads&lt;/a&gt;, an approach to forcing JavaScript to perform multiple tasks at (approximately) the same time.&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%2Fdlleit3m45i92qht169u.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%2Fdlleit3m45i92qht169u.png" title="Parallel threads" alt="Parallel threads"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, these are some quick notes on getting worker threads…well, &lt;em&gt;working&lt;/em&gt;. It was easy enough to make it work, but there are some points where it’s unclear what is supposed to happen, with “minimal example” code all having strange and unnecessary features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Threads, in General
&lt;/h2&gt;

&lt;p&gt;Originally, Sun Microsystems created what they called “light-weight processes,” a system where multiple code-paths can run in parallel within the same program or processes. As other languages implemented similar approaches, the term evolved into “threads.”&lt;/p&gt;

&lt;p&gt;If multiple threads are run under the same process, this typically gives benefits over a multi-process approach with interprocess communication, since most of the system state can be shared, saving overhead on context switches and thread creation. If you haven’t taken an operating systems course and don’t recognize those terms, they basically boil down to not needing to keep pausing and restarting programs, since everything should be running from the same package.&lt;/p&gt;

&lt;p&gt;Generally speaking, threads have a handful of common operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create&lt;/strong&gt; sets up the new thread and assigns it a workload and initial data to work with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit&lt;/strong&gt; ends the thread from the inside, leaving the data to be harvested by the main program.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join&lt;/strong&gt; takes the data from the ended thread to make it available to the main program.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s not the &lt;em&gt;entire&lt;/em&gt; model, of course. There are a lot of utility features allowing the programmer to set different parameters and retrieve information, but the core process is create-exit-join.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worker Threads
&lt;/h2&gt;

&lt;p&gt;Node’s worker threads…aren’t that.&lt;/p&gt;

&lt;p&gt;In some ways, it makes sense. The standard approach to threading goes back to the early 1990s, and it’s now almost thirty years later, so maybe we’ve learned some things that make life easier. And then again…well, we’ll see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thread Creation
&lt;/h3&gt;

&lt;p&gt;We launch a thread &lt;em&gt;almost&lt;/em&gt; normally, but with a twist that makes me extremely suspicious about how this all works under the covers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker_threads&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;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./workercode.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;workerData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;someObjectWithInitialData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typically, threads are given functions to run. Worker threads are different, though, taking a &lt;em&gt;file&lt;/em&gt;. This is where suspicion starts to come in, as sending execution to a separate file implies that the thread is a separate program, rather than a single program sharing state.&lt;/p&gt;

&lt;h4&gt;
  
  
  Thread Handlers
&lt;/h4&gt;

&lt;p&gt;The worker thread has three events we can chose to handle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&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;acceptUpdate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&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;reportUpdateError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exit&lt;/span&gt;&lt;span class="dl"&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;reportExit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each handler function takes a single parameter. The message can be an arbitrary object. The error is a JavaScript &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error" rel="noopener noreferrer"&gt;&lt;code&gt;Error&lt;/code&gt;&lt;/a&gt; object. The exit code is an integer.&lt;/p&gt;

&lt;p&gt;There is also an &lt;em&gt;online&lt;/em&gt; handler, announcing when the thread has started execution, taking no parameters, if that’s useful to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Returning Data
&lt;/h3&gt;

&lt;p&gt;Worker threads don’t really exit and join, though I suppose an exit value could be used to simulate that. Instead, the thread takes its initial state from a default &lt;code&gt;workerData&lt;/code&gt; variable (imported from the &lt;code&gt;worker_threads&lt;/code&gt; library) and sends messages back to the main thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;parentPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;workerData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker_threads&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;parentPort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;someObjectWithResults&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message handler (&lt;code&gt;acceptUpdate()&lt;/code&gt;, in the example above) then receives a copy of &lt;code&gt;someObjectWithResults&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This also works in the opposite direction, with the main thread sending messages to the worker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateForTheThread&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are surprising improvements over traditional threading libraries, because it allows the thread to easily send and &lt;em&gt;receive&lt;/em&gt; updates whenever it gets them instead of waiting until it’s out of work to return everything it has collected or messing around in shared memory. &lt;em&gt;However&lt;/em&gt;, this still smells of running in a separate process, basically treating the thread as a peer to coordinate with across a network connection or a special kind of shared file called a “pipe” that I won’t bother to discuss, here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Join
&lt;/h3&gt;

&lt;p&gt;All that said, we still get a traditional join operation, where the main thread can harvest data from the worker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeapSnapshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This call fails unless the thread has exited, meaning that it’s best run in the exit handler (&lt;code&gt;reportExit()&lt;/code&gt;, in the example above), and makes the worker threads feel less like a separate process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Further
&lt;/h2&gt;

&lt;p&gt;So, after all that, I’m still not 100% convinced that worker threads are &lt;em&gt;actually&lt;/em&gt; threads, but they seem to mostly do the job and that’s mostly what matters.&lt;/p&gt;

&lt;p&gt;There’s actually a lot more available, here, too. The threads can communicate through console I/O. A thread can set up additional communications channels, which can be passed to the parent for another thread, allowing two worker threads to communicate directly. Ports (endpoints to a communications channel) can be manipulated to prevent the thread from exiting, and so forth.&lt;/p&gt;

&lt;p&gt;Like I said, though, we have our basic create-exit-join model &lt;em&gt;plus&lt;/em&gt; communication back and forth, which is fairly useful for a lot of kinds of work. If they’re not “really” threads, it doesn’t matter much, as long as the code doesn’t block and they basically act like threads.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is &lt;a href="https://www.flickr.com/photos/ndanger/2744507570/in/photolist-5bwju3-oHmhfe-azNZVL-nRD4oA-9Av7cj-9Av72w-9AscDz-8wm2jQ-dXJied-dwoaAQ-e9hWJ3-5WvZrj-7x5JHd-bAQF6a-5prZKP-UtHaz4-KRhP9J-bparyB-UBPvhy-3Sccxf-3ScbYu-BNk4ye-KFJvL8-31V1f-jSbB3-2hPw6RT-2hPC4Gf-2hPVhpW-23myQcm-2hPJdj7-2hPTp9W-2hPJetM-2hQ33FL-2hPfRAc-2hPgXqs-2hPzVN2-23EyPNh-ripaJo-rUyG2-HJPhB-873DBk-bAFsAn-aE3xwB-8vHt7g-73p4V4-oh8NZ1-873EQT-iiLRjP-876Ne9-kJLWEe" rel="noopener noreferrer"&gt;Threads&lt;/a&gt; by &lt;a href="https://www.flickr.com/photos/ndanger/" rel="noopener noreferrer"&gt;Dave Gingrich&lt;/a&gt; and made available under the terms of the &lt;a href="https://creativecommons.org/licenses/by-sa/2.0/" rel="noopener noreferrer"&gt;Creative Commons Attribution Share-Alike 2.0 Generic license&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>threads</category>
    </item>
    <item>
      <title>Recutils, the Plain-Text Database</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Sat, 28 Mar 2020 13:48:46 +0000</pubDate>
      <link>https://dev.to/jcolag/recutils-the-plain-text-database-52ma</link>
      <guid>https://dev.to/jcolag/recutils-the-plain-text-database-52ma</guid>
      <description>&lt;p&gt;I originally posted a version of this to &lt;a href="https://john.colagioia.net/blog/2020/02/05/recutils.html"&gt;my blog&lt;/a&gt; as the first of my &lt;a href="https://john.colagioia.net/blog/tags/techtips"&gt;"tech tips"&lt;/a&gt; that are 90% notes to myself so that I don't need to keep researching.  To my surprise, it has become one of the most popular posts, there, so maybe it'll be useful to someone here, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background on Databases
&lt;/h2&gt;

&lt;p&gt;Before I jump into &lt;em&gt;recutils&lt;/em&gt; itself, if you're not already conversant in databases, I'll try to give a capsule version.  For example, If you've taken the undergraduate database class or have written more than a few lines of SQL, you can probably safely skip down to &lt;strong&gt;Recutils, in General&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For the rest of you, here are some definitions, and then I'll go over some of the basics.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;database&lt;/strong&gt; is a collection of related data, regardless of the form.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;relational database&lt;/strong&gt; is a database (surprise!) where we store data elements based on their relationships (surprise again!) to other elements.  Sometimes those relationships are described as records and sometimes they're described by metadata that connects separate records.  The full definition centers on &lt;a href="https://en.wikipedia.org/wiki/Codd's_12_rules"&gt;Codd's Rules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;database management system&lt;/strong&gt; (DBMS) is software that mediates access to a database to minimize the chances of a user or program shooting itself in the foot.  For example, it might (depending on the system) moderate concurrent access or prevent you from deleting a record that another record points to.&lt;/p&gt;

&lt;p&gt;Every database has a &lt;strong&gt;universe of discourse&lt;/strong&gt; or &lt;strong&gt;mini-world&lt;/strong&gt; that explains how to interpret the data, whether anybody bothered to write it down.  We all somehow agree to never talk about this, even though "what does this data actually mean?" is one of the most important questions we can be asking, so I'm bringing it up here...&lt;/p&gt;

&lt;p&gt;Instead of a universe of discourse, the thing we really get is a &lt;strong&gt;schema&lt;/strong&gt;, the layout of what we store in each kind of record and how they relate to each other through foreign keys.  I'll explain foreign keys in a bit, rather than try to assemble a coherent definition.&lt;/p&gt;

&lt;p&gt;With that out of the way, record sets are called "relations" by theoreticians and implemented as &lt;em&gt;tables&lt;/em&gt;, with records implemented as &lt;em&gt;rows&lt;/em&gt; and individual elements as &lt;em&gt;columns&lt;/em&gt;.  The terms are mostly interchangeable, but you'll find table/row/column used by just about everybody in industry.&lt;/p&gt;

&lt;p&gt;The last thing you probably need to know (unless you want to implement a DBMS) is that finding data ("querying") has what amounts to three steps.&lt;/p&gt;

&lt;p&gt;First, we &lt;strong&gt;join&lt;/strong&gt; the relations/tables we need, by which I mean taking records/rows that are connected through a common element/field/column.  That commonality is (and should be specified as) a "foreign key," a kind of pointer into that other table's space.  Even if we only care about one table, we still need to specify it.&lt;/p&gt;

&lt;p&gt;Second, we &lt;strong&gt;filter&lt;/strong&gt; the available records down to those whose elements are interesting to us, very similar to writing an &lt;code&gt;if&lt;/code&gt; statement in general programming.  We might want all the records, of course, which makes this step trivial.  Or this might be extremely complicated, depending on what we want.&lt;/p&gt;

&lt;p&gt;Third, we &lt;strong&gt;project&lt;/strong&gt; the remaining records to just the fields that interest us.  Again, we might want every field/column, making this part easier to specify.&lt;/p&gt;

&lt;p&gt;If you're familiar with SQL, you'll probably recognize the &lt;code&gt;SELECT column-or-columns FROM table-or-tables WHERE conditions-are-true&lt;/code&gt; format, where the &lt;code&gt;FROM&lt;/code&gt; might list tables arbitrarily or might explicitly use &lt;code&gt;JOIN&lt;/code&gt; to connect the tables.  If you're not, &lt;em&gt;recutils&lt;/em&gt; doesn't use SQL, so don't worry about it.&lt;/p&gt;

&lt;p&gt;Now that we're all up to speed on databases, I can rant for a bit about why I care about &lt;em&gt;recutils&lt;/em&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  Recutils, in General
&lt;/h2&gt;

&lt;p&gt;When I store information, I'm a big fan of light-weight, text-based formats for a variety of reasons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They store and compress well, when that's important.&lt;/li&gt;
&lt;li&gt;It's possible to quickly inspect or edit whatever I'm working on.&lt;/li&gt;
&lt;li&gt;I can quickly search anything I'm working on with a tool like &lt;a href="https://en.wikipedia.org/wiki/Grep"&gt;&lt;code&gt;grep&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I'm not tied to specific applications.&lt;/li&gt;
&lt;li&gt;It's easier to convert to another format, making it closer to future-proof.&lt;/li&gt;
&lt;li&gt;Version control understands text better than anything else, so I can easily analyze changes over time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, &lt;a href="https://github.com/jcolag/entropy-arbitrage"&gt;my blog&lt;/a&gt;, &lt;a href="https://github.com/jcolag/silver-bat-01-seeking-refuge"&gt;my novel&lt;/a&gt;, and my personal notes are generally all written in &lt;a href="https://www.markdownguide.org/"&gt;Markdown&lt;/a&gt; files.  I store most of my spreadsheets as sets of &lt;a href="https://en.wikipedia.org/wiki/Comma-separated_values"&gt;CSV&lt;/a&gt; files, unless the formatting or calculated cells are important.  In the rare cases I need something like a flow chart, it's usually in &lt;a href="http://floppsie.comp.glam.ac.uk/Glamorgan/gaius/web/pic.html"&gt;pic&lt;/a&gt; format.&lt;/p&gt;

&lt;p&gt;Databases don't usually do any of this well, however.  For the sake of efficiency, you typically dump all of your data into a binary file that the database management system has optimized for its own access and analytical purposes.&lt;/p&gt;

&lt;p&gt;So, I got a little bit excited when I discovered &lt;a href="https://labs.tomasino.org/gnu-recutils/"&gt;&lt;em&gt;recutils&lt;/em&gt;&lt;/a&gt;, a system to manage plain-text relational databases.  It's not perfect (unless I'm missing something important), but it's definitely close to the sort of thing I would generally reach for, closer than using a free-form &lt;a href="https://en.wikipedia.org/wiki/JSON"&gt;JSON&lt;/a&gt; file, for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  File Organization
&lt;/h2&gt;

&lt;p&gt;Unlike a traditional relational database management system, &lt;em&gt;recutils&lt;/em&gt; needs you to make decisions about where to store records.  Normally, the files are obscured from your vision, if only so that you're not tempted to monkey around with the content in an editor.  Here, however, you need to decide what files you want and what you want to store in each.&lt;/p&gt;

&lt;p&gt;And in fact, one of the clunkier aspects to &lt;em&gt;recutils&lt;/em&gt; that I found is that it's difficult (impossible?) to work with an arbitrary subset of files, such as asking it to operate on two (and only two) files at once.  So, there seem to be two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put all the record types into one file, or&lt;/li&gt;
&lt;li&gt;Separate the record types into different files, while specifying &lt;em&gt;all&lt;/em&gt; of them with a wildcard (such as "&lt;code&gt;*.rec&lt;/code&gt;") whenever working in situations that require more than one record type (like a &lt;a href="https://en.wikipedia.org/wiki/Relational_algebra#Joins_and_join-like_operators"&gt;Join&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither of these is ideal, of course, but the single file is probably acceptable for small databases and the latter is probably the most intuitive.  I'll use the one-record-type-per-file approach, here.&lt;/p&gt;

&lt;p&gt;Obviously, &lt;em&gt;recutils&lt;/em&gt; doesn't actually care either way, and it's possible to combine the two, so that record types that are often used together can share a file, while the other clusters of types or individual types can be in their own files.  You don't even really need to separate the files by type, and instead could assign records to files by any criteria you like, like your favorite color at the moment you're adding the records.  You shouldn't do that, because it will make your life harder, but &lt;em&gt;recutils&lt;/em&gt; isn't going to care, is my point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Record Schema
&lt;/h2&gt;

&lt;p&gt;While it's not strictly necessary to have a schema for records to conform to, it makes life significantly easier, if for no other reason than having some documentation.  So, the header of a file of records, optionally, shows the types of each field, keys, and so forth.&lt;/p&gt;

&lt;p&gt;It looks to me like unspecified fields are all arbitrary strings, multi-line with a plus sign (&lt;code&gt;+&lt;/code&gt;) at the beginning of every line after the first.  Other than that, if we are looking to store something like application users, we might have something like...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The name of the "table" and a human-readable
# description
%rec: user
%doc: User information

# The non-multi-line string fields
%type: id int
%type: login line
%type: email email
%type: password line
%type: name line
%type: created_at: date
%type: updated_at: date
%type: role enum Guest User Moderator Administrator
%type: deleted bool

# Roles for fields; note that we sneak in "profile,"
# which is our multi-line string.
%key: id
%mandatory: login email password
%allowed: name profile created_at updated_at role deleted
# We could also %prohibit fields we want to ensure don't
# exist until we're ready to set them.

# Special restrictions
%auto: id created_at updated_at
%confidential: password
%unique: login email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's probably more extensive than it needs to be for an example, but we see the record type name (&lt;code&gt;%rec&lt;/code&gt;), decent coverage of the available types (&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;line&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;date&lt;/code&gt;, &lt;code&gt;enum&lt;/code&gt;, and &lt;code&gt;bool&lt;/code&gt;, the different kinds of fields (other than &lt;code&gt;prohibited&lt;/code&gt;), and constraints (&lt;code&gt;%auto&lt;/code&gt;, &lt;code&gt;%confidential&lt;/code&gt;, and &lt;code&gt;%unique&lt;/code&gt;).  I expect all of them are self-explanatory, except maybe &lt;code&gt;line&lt;/code&gt; for a single line of text and &lt;code&gt;confidential&lt;/code&gt; encrypting the field.&lt;/p&gt;

&lt;p&gt;There's one useful type &lt;em&gt;not&lt;/em&gt; shown here, that I'll get to in a bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Records
&lt;/h2&gt;

&lt;p&gt;Each &lt;code&gt;user&lt;/code&gt; record, now, is going to need to have our specified fields and the right types, all in &lt;code&gt;name: value&lt;/code&gt; format.  For example, I created the following user via the &lt;a href="https://www.fakenamegenerator.com/"&gt;Fake Name Generator&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id: 7760
login: Brouch
email: VanessaJStolz@dayrep.com
password: $2a$11$WBkGS/r5/0tn2muqPXreVO40wy6dxdEzKj/lY5.y7BQ5YoOHKerNW
name: Vanessa J. Stolz
profile: A 78-year-old Libra from Atlanta, Georgia, who works
+ for De Pinna as a machine feeder and drives a blue 2000
+ Toyota Pod.
created_at: 2020-02-05 06:48:55-0500
updated_at: 2020-02-05 06:48:55-0500
role: Moderator
deleted: False
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a brief aside, &lt;em&gt;recutils&lt;/em&gt; doesn't care about extra fields.  So, if you want to add a birthday, significant other's name, and hat size to Vanessa's record above, but not anybody else's, that's probably going to be somewhat difficult to work with, but not destructive.&lt;/p&gt;

&lt;p&gt;Beyond just editing our &lt;code&gt;users.rec&lt;/code&gt; file manually, we have a few options to populate our file with data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conversion
&lt;/h3&gt;

&lt;p&gt;If you have a pre-existing data source, &lt;em&gt;recutils&lt;/em&gt; comes with two utilities to convert from common database types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;csv2rec&lt;/code&gt;&lt;/strong&gt; turns each line of a CSV file into a record, with the header line providing the field names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mdb2rec&lt;/code&gt;&lt;/strong&gt; converts a &lt;a href="https://en.wikipedia.org/wiki/Microsoft_Access"&gt;Microsoft Access&lt;/a&gt; database (or a single table) into records.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can confirm that the CSV program works well, but couldn't find an Access database to test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insertion
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;recins&lt;/code&gt;&lt;/strong&gt; utility, probably unsurprisingly, inserts records.  Continuing with our users, it can be used something like...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;recins &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;user &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;login &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Gode1962 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;email &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;PhillipSCollins@teleworm.us &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;password &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;name &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Phillip S. Collins"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;profile &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Suburban driver of Dodge Rams"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;role &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;User &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--field&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;deleted &lt;span class="nt"&gt;--value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;False &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--verbose&lt;/span&gt; users.rec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming that none of the constraints have been violated, here, this will add the record to the &lt;code&gt;users.rec&lt;/code&gt; file.  If it fails, the &lt;code&gt;--verbose&lt;/code&gt; argument will produce an error message explaining what went wrong.&lt;/p&gt;

&lt;p&gt;If you have one record definition/schema in each file, &lt;code&gt;recins&lt;/code&gt; will figure out where to store it, thankfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring
&lt;/h2&gt;

&lt;p&gt;We can get some basic information, now.  The following gives us a count of each record type in 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;recinf users.rec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference between that and retrieving the schema is a matter of an argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;recinf &lt;span class="nt"&gt;--descriptor&lt;/span&gt; users.rec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then we can check into more general queries of the data, though the syntax isn't SQL.  We can pick a random set of records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;recsel &lt;span class="nt"&gt;--random&lt;/span&gt; 5 users.rec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can get a "projection" of all data by specifying columns.  The columns can also be renamed for convenience, which will be important, later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;recsel &lt;span class="nt"&gt;--print-values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;login,email users.rec
recsel &lt;span class="nt"&gt;--print-values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;login:Username,email:EMail users.rec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replacing &lt;code&gt;--print-values&lt;/code&gt; with just &lt;code&gt;--print&lt;/code&gt; produces output that looks like a record, with &lt;code&gt;name: value&lt;/code&gt; pairs.  Also, if there are multiple types of records, we would need to specify &lt;code&gt;--type=user&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can also run arbitrary queries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;recsel &lt;span class="nt"&gt;--expression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"id = 23573"&lt;/span&gt; users.rec
recsel &lt;span class="nt"&gt;--expression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"email ~ gmail.com"&lt;/span&gt; users.rec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expressions can get &lt;a href="https://www.gnu.org/software/recutils/manual/Selection-Expressions.html#Selection-Expressions"&gt;fairly sophisticated&lt;/a&gt; and this doesn't support SQL's syntax.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deleting Records
&lt;/h2&gt;

&lt;p&gt;Now that we understand insertion and query expressions, we should be able to figure out how to delete a record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;recdel &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;user &lt;span class="nt"&gt;--expression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"login = Selits"&lt;/span&gt; &lt;span class="nt"&gt;--comment&lt;/span&gt; users.rec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--comment&lt;/code&gt; argument deletes the record by commenting it out in the file, so that it can be found or restored later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Tables
&lt;/h2&gt;

&lt;p&gt;Now that we've settled our users, we can give the users something to work with, like favorite websites.  We'll keep this &lt;code&gt;website.rec&lt;/code&gt; schema simpler than our users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%rec: website
%doc: Favorite websites submitted to the database.

%type: id int
%type: url line
%type: owner rec user
%type: created_at: date
%type: updated_at: date

%key: id
%mandatory: url owner

%auto: id created_at updated_at
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important field, here, sets up a foreign key:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;%type: owner rec user&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is, &lt;code&gt;owner&lt;/code&gt; is a reference to a record of type &lt;code&gt;user&lt;/code&gt;.  Because the &lt;code&gt;user&lt;/code&gt; record type's key is an integer, the &lt;code&gt;owner&lt;/code&gt; field is an integer, as well.&lt;/p&gt;

&lt;p&gt;So, we have an example record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;id: 2031
url: https://www.gnu.org/software/recutils/manual/Foreign-Keys.html#Foreign-Keys
owner: 7760
created_at: 2020-02-05 06:48:55-0500
updated_at: 2020-02-05 06:48:55-0500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;7760&lt;/code&gt;, in this case, maps this URL to the &lt;code&gt;user&lt;/code&gt; table through its key, the &lt;code&gt;id&lt;/code&gt; field.&lt;/p&gt;

&lt;h3&gt;
  
  
  Joining Tables
&lt;/h3&gt;

&lt;p&gt;Now, we get to the important stuff.  We can now get a list of URLs and the name of the person who submitted each one.  Remember, we can't explicitly specify multiple files, so we either have a single file or a wildcard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;recsel &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;website &lt;span class="nt"&gt;--field&lt;/span&gt; owner &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--print-values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;url:URL,owner_name:Owner &lt;span class="k"&gt;*&lt;/span&gt;.rec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, of course, we could add a query expression to the command, if we wanted, or we could involve additional tables.&lt;/p&gt;

&lt;p&gt;Note, incidentally, that the join is an &lt;em&gt;inner&lt;/em&gt; join.  One of the problems with &lt;em&gt;recutils&lt;/em&gt; seems to be that other kinds of joins are not permitted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming Support
&lt;/h2&gt;

&lt;p&gt;In the grand scheme, a database that only allows &lt;em&gt;ad hoc&lt;/em&gt; queries isn't particularly useful.  Instead, we need some kind of API that can be used by software.  This is only an API if you're writing shell scripts, obviously.&lt;/p&gt;

&lt;p&gt;For programming in &lt;strong&gt;C&lt;/strong&gt;, there's the &lt;em&gt;librec-dev&lt;/em&gt; library package, and a lot of other languages can (indirectly) use that.  But I only see bindings available for &lt;a href="https://github.com/maninya/python-recutils"&gt;Python&lt;/a&gt; and &lt;a href="https://github.com/dineshkummarc/PHPRecutils"&gt;PHP&lt;/a&gt;, with neither library updated in several years.&lt;/p&gt;

&lt;p&gt;Also, the library documentation is "go read the source code," specifically &lt;a href="https://git.savannah.gnu.org/cgit/recutils.git/tree/src/rec.h"&gt;the main header file&lt;/a&gt;.  Apparently, the important functions in the library approximately map to...&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;librec Function&lt;/th&gt;
&lt;th&gt;recutils Utility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rec_db_query&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;recsel&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rec_db_insert&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;recins&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rec_db_delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;recdel&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rec_db_set&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;recset&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rec_int_check_db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;recfix --check&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's not spectacular, but still has some potential to improve, at least.  Maybe at some point, when other things settle down, I'll revisit &lt;em&gt;recutils&lt;/em&gt; specifically to document the library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Capabilities
&lt;/h2&gt;

&lt;p&gt;This just scratches the surface, honestly.  Queries can be grouped.  Aggregate functions are available.  Subtypes are available, such as strings that conform to a regular expression or integers in a limited range, and can be named with &lt;code&gt;%typedef&lt;/code&gt;.  Records can have compound fields or be sorted.  There are more encryption options than just &lt;code&gt;%confidential&lt;/code&gt;.  Output can be formatted with templates.  Records can be updated.  The &lt;strong&gt;&lt;code&gt;recfix&lt;/code&gt;&lt;/strong&gt; program performs diagnostics and repairs some issues.&lt;/p&gt;

&lt;p&gt;In other words, it's a sophisticated system with a lot going for it, despite the handful of problems I've called out in the preceding discussion.  It won't replace CSV files for my simple data sets, but for anything more complicated than that (other than calculation, which still needs a lightweight solution that doesn't have the overhead of a typical programming language), it looks pretty good!&lt;/p&gt;

&lt;p&gt;As a good example of where I'll start using it, for the &lt;a href="https://john.colagioia.net/blog/fiction/2019/12/14/seeking-refuge.html"&gt;Silver Bat&lt;/a&gt; stories (more in that universe are definitely coming), I currently just have a Markdown file that lists all the characters.  It includes their profiles from my profile generator, plus a brief biography and possible plot threads.  When the time comes to share that information with other people, it would be nice to manage that data better and also turn it into a kind of concordance to be able to see where all the characters have been used.&lt;/p&gt;

&lt;p&gt;For much more information, you can read through the &lt;a href="https://www.gnu.org/software/recutils/manual/"&gt;documentation&lt;/a&gt;.  It's not particularly friendly to newcomers who don't already understand how things work---which is hopefully nobody who got to the end of this post---but it's extensive enough to answer most questions that aren't related to the library.&lt;/p&gt;

&lt;p&gt;All things considered, this seems like a decent foundation for projects where transparency is important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus:  SQLite Export
&lt;/h2&gt;

&lt;p&gt;Since I wanted to get up and running quickly with data with &lt;em&gt;recutils&lt;/em&gt;, I decided to use a database I was already working with to populate my records.  SQLite, which is where the data is, has a mildly clunky way of exporting tables to CSV files.&lt;/p&gt;

&lt;p&gt;First, get into the SQLite shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlite3 database.sqlite3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, decide what you want to export and go for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;This&lt;/span&gt; &lt;span class="n"&gt;lists&lt;/span&gt; &lt;span class="k"&gt;all&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Now&lt;/span&gt; &lt;span class="n"&gt;that&lt;/span&gt; &lt;span class="n"&gt;we&lt;/span&gt; &lt;span class="n"&gt;know&lt;/span&gt; &lt;span class="n"&gt;what&lt;/span&gt; &lt;span class="n"&gt;tables&lt;/span&gt; &lt;span class="k"&gt;are&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;we&lt;/span&gt; &lt;span class="n"&gt;can&lt;/span&gt; &lt;span class="n"&gt;export&lt;/span&gt; &lt;span class="n"&gt;them&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;mode&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;The&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="k"&gt;names&lt;/span&gt; &lt;span class="n"&gt;don&lt;/span&gt;&lt;span class="s1"&gt;'t need to match.
.output websites.csv
SELECT * FROM websites;
# If you don'&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="n"&gt;directive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="k"&gt;all&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="n"&gt;will&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;which&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;going&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="n"&gt;everything&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;more&lt;/span&gt;
&lt;span class="o"&gt;#&lt;/span&gt; &lt;span class="n"&gt;complicated&lt;/span&gt; &lt;span class="k"&gt;than&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;needs&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we have files that can be converted to record sets with &lt;strong&gt;&lt;code&gt;csv2rec&lt;/code&gt;&lt;/strong&gt; as described above.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt;:  The header image is &lt;a href="https://pxhere.com/en/photo/606288"&gt;work, technology, vintage, wheel, retro, clock&lt;/a&gt;, by an anonymous PxHere photographer, is made available under the terms of the &lt;a href="https://creativecommons.org/publicdomain/zero/1.0/"&gt;CC0 1.0 Universal Public Domain Dedication&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>database</category>
      <category>plaintext</category>
      <category>linux</category>
    </item>
    <item>
      <title>Metal Umlauts, Searching, and Other Unicode Fun</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Wed, 25 Mar 2020 13:38:28 +0000</pubDate>
      <link>https://dev.to/jcolag/metal-umlauts-searching-and-other-unicode-fun-1e2b</link>
      <guid>https://dev.to/jcolag/metal-umlauts-searching-and-other-unicode-fun-1e2b</guid>
      <description>&lt;p&gt;(You can find the original version of this article &lt;a href="https://john.colagioia.net/blog/programming/2019/12/17/metal-umlauts.html"&gt;on my blog&lt;/a&gt;, where I talk about this and a variety of other topics.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Unicode"&gt;Unicode&lt;/a&gt;—the computer “alphabet” that includes all the characters you see on this page, plus most modern writing systems in common use (∂), plus punctuation and currency (௹), plus arrows and mathematical notation (↛), plus drawing symbols (✵), plus emoji (🐣), and more—has a lot going on in it beyond the obvious complexity of multiple formats (UTF-8, UTF-16, GB18030, UTF-32, BOCU, SCSU, UTF-7, and probably others) and &lt;a href="https://simple.wikipedia.org/wiki/Endianness"&gt;byte orderings&lt;/a&gt;.  The part that grabbed my interest, recently, is the idea of &lt;a href="https://en.wikipedia.org/wiki/Unicode_equivalence#Normalization"&gt;Normal Forms&lt;/a&gt;, of which Unicode has four.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NFD&lt;/strong&gt;: Canonical Decomposition&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFC&lt;/strong&gt;:  Canonical Composition&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFKD&lt;/strong&gt;:  Compatibility Decomposition&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFKC&lt;/strong&gt;:  Compatibility Composition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Specifically, Normalization Form Canonical Decomposition interests me, because it represents each accented letter in a string as the base letter followed by any accents.&lt;/p&gt;

&lt;p&gt;Better yet, in JavaScript (and more languages; see below), it’s easy to change normalization forms.  Specifically, for these purposes, we want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NFD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These decomposed letters have some nice uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sorting
&lt;/h2&gt;

&lt;p&gt;At least in English, diacritical marks are usually a marker for either history (fiancée, über, soupçon, Māori, piñata) or pronunciation (naïve, coöperate), rather than as an element of spelling; some of us are sticklers for getting the accents right, but most English-speakers ignore them completely.  This is especially true of names, where we generally want a person’s name to be &lt;em&gt;represented&lt;/em&gt; properly out of respect (Karel Čapek, Charlotte Brontë, Beyoncé Knowles), when that name can come from anywhere in the world, but English treats it more as an affectation than a critical element of the name.&lt;/p&gt;

&lt;p&gt;Of particular importance, here, is that we generally wish to sort a name with accented letters as if the accents don’t exist.  So, we want piñata to sort as if it was spelled “pinata” and Čapek to sort like “Capek.”&lt;/p&gt;

&lt;p&gt;The decomposed form allows us to do this by stripping the diacritical marks out of the string when we sort it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sortedStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&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;var&lt;/span&gt; &lt;span class="nx"&gt;aNorm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NFD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\u&lt;/span&gt;&lt;span class="sr"&gt;0300-&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;036f&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bNorm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NFD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\u&lt;/span&gt;&lt;span class="sr"&gt;0300-&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="sr"&gt;036f&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&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;aNorm&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;bNorm&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;That admittedly looks a bit complicated, given the regular expression, but the entire process boils down to decomposing each string, and stripping off the diacritical marks (Unicode code-points 0x0300 to 0x036f), and converting the remaining letters to lower-case.  Then, we just compare the resulting strings.&lt;/p&gt;

&lt;p&gt;In other words, by normalizing the name, the computer represents “Čapek” something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[C] [caron] [a] [p] [e] [k]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we remove any diacritical marks (the &lt;a href="https://en.wikipedia.org/wiki/Caron"&gt;caron&lt;/a&gt; or &lt;strong&gt;ˇ&lt;/strong&gt;   in this case) by replacing it with nothing, leaving us with only the unaccented Latin letters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Or…
&lt;/h3&gt;

&lt;p&gt;I can't think of a use for this idea, but it occurs to me that it's also possible to &lt;em&gt;keep&lt;/em&gt; the diacritical marks and throw out or replace the letters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching
&lt;/h2&gt;

&lt;p&gt;More so than with sorting, it’s also a better experience to search without regard for diacritical marks.  For example, an increasing number of laws (with political motivations that we don't need to discuss, here) are posed as “exact match” measures, which require that voter registration documents transcribed from handwritten forms be identical to personal identification documents, meaning that the &lt;em&gt;exactness&lt;/em&gt; of accents and diacritical marks relies primarily on the comprehension and interest of an underpaid, overworked data entry clerk using a keyboard that doesn't have accents on it.&lt;/p&gt;

&lt;p&gt;By the same token, even something with much lower stakes like searching an employee directory shouldn’t rely on the person searching for Beyoncé realizing that she has an acute accent in her name &lt;em&gt;and&lt;/em&gt; that Human Resources input her name properly.&lt;/p&gt;

&lt;p&gt;And that just barely touches on the problem that a standard keyboard for English doesn’t have a way to type accented characters, with operating systems often adding ways that aren't exactly trivial.  So, even if a user has cleared the above hurdles, it’s still a waste of the user’s time to make them hunt down the exact spelling with diacritical marks.&lt;/p&gt;

&lt;p&gt;We can solve this problem using an approach similar to what we saw in sorting, normalizing and stripping both the target string and the corpus being searched.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metal Umlauts (or M͇ͭeţal Um͆l̼a͍u̓t̨s)
&lt;/h2&gt;

&lt;p&gt;It’s a bit before my time, but one of my favorite television shows growing up (via re-runs and now streaming) is &lt;a href="https://en.wikipedia.org/wiki/Mission:_Impossible_(1966_TV_series)"&gt;&lt;strong&gt;Mission: Impossible&lt;/strong&gt;&lt;/a&gt;, in no small part because of the signage in their fictional foreign countries.  Especially in earlier episodes, to make foreign countries seem both exotic and approachable to American audiences, show creator Bruce Geller had the idea of creating signs written mostly in English, but a version of English with clever misspellings representative of stereotypes of certain parts of the world, often including bogus diacritical marks.&lt;/p&gt;

&lt;p&gt;For example, if you pay careful attention, you’ll easily spot both &lt;em&gt;Zöna Restrik&lt;/em&gt; (for Restricted Area) or &lt;em&gt;Prıziion Mılıtık&lt;/em&gt; (for Military Prison) in certain episodes.&lt;/p&gt;

&lt;p&gt;And, of course, if you’re a heavy metal music fan, you’re undoubtedly familiar with the similar but distinct &lt;a href="https://en.wikipedia.org/wiki/Metal_umlaut"&gt;Metal Umlaut&lt;/a&gt;, though its use seems surprisingly limited to the diaeresis (&lt;strong&gt;¨&lt;/strong&gt;) mark.&lt;/p&gt;

&lt;p&gt;If we wanted to do something like transforming English text to "Gellerese"…well, you’re on your own figuring out how to change the base spelling in a reasonable way.  But adding bogus diacritical marks?  That, we can definitely do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NFD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The math on the next line isn't necessary to the example;&lt;/span&gt;
    &lt;span class="c1"&gt;// I'll explain what it's for in the paragraph below.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;j&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="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;rLen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rCh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x0300&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mh"&gt;0x006f&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rCh&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, we normalize the input string.  But instead of removing diacritical marks as we’ve been doing, here we visit each character and, if it’s a letter, we pick a random-but-small number of diacritical marks to &lt;strong&gt;add&lt;/strong&gt; (using &lt;code&gt;log2()&lt;/code&gt; pushes the numbers lower and biases the distribution towards the lower end, so we're more likely to get zero or one mark, but can potentially get more), and then selects the necessary diacritical marks from that same 0x0300 to 0x036f range we previously needed to remove.&lt;/p&gt;

&lt;p&gt;If desired, this can easily be made more “intelligent” with lists of diacritical marks that are more appropriate to that letter, so that you don’t end up with implausible combinations like what you see in the above section heading.&lt;/p&gt;

&lt;p&gt;While this sounds like just a joke or a tool for fiction, I now sometimes use techniques like this to make sure that diacritical marks display properly after processing text.  By generating them randomly, in bulk, and in ways not generally found in real text, I get a better sense of how bad a display might look.&lt;/p&gt;

&lt;p&gt;In any case, it might be a decent idea to call &lt;code&gt;output.normalize('NFC')&lt;/code&gt; at the end, to set the characters back to their “composed” forms.  And when I say “decent idea,” I mean “probably not necessary, but nice for the sake of consistency.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Exception
&lt;/h2&gt;

&lt;p&gt;One place where normalization has no effect is the Polish &lt;em&gt;L-with-stroke&lt;/em&gt; (Ł or ł).  It turns out that those are letters unto themselves, rather than letters with a diacritical mark.  So, if you’re planning on using any of these techniques, you will want to take that into account, probably by replacing the character separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other (Programming) Languages
&lt;/h2&gt;

&lt;p&gt;The above sample code snippets are all in JavaScript, but the Windows API supports &lt;a href="https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-normalizestring"&gt;&lt;code&gt;NormalizeString()&lt;/code&gt;&lt;/a&gt; and .NET has supported &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.string.normalize?view=netframework-4.8"&gt;&lt;code&gt;String.Normalize()&lt;/code&gt;&lt;/a&gt; for quite some time.  Ruby, similarly, supports &lt;a href="https://apidock.com/ruby/v2_5_5/String/unicode_normalize"&gt;&lt;code&gt;string.unicode_normalize()&lt;/code&gt;&lt;/a&gt;.  It shouldn’t be hard to find the equivalent for other languages, now that we know the key words to search for are “unicode normalize,” maybe throwing in “nfd” or “decomposed” to make the context clearer.&lt;/p&gt;

&lt;p&gt;Happy…err, umlauting?  Sure.  Let’s go with that!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt;:  Untitled header photograph &lt;a href="https://pxhere.com/en/photo/629962"&gt;from PxHere&lt;/a&gt;, made available under the &lt;a href="https://creativecommons.org/publicdomain/zero/1.0/"&gt;CC0 1.0 Universal Public Domain Dedication&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>unicode</category>
      <category>javascript</category>
      <category>diacriticals</category>
      <category>ux</category>
    </item>
    <item>
      <title>Small-D date Night</title>
      <dc:creator>John Colagioia (he/him)</dc:creator>
      <pubDate>Wed, 18 Mar 2020 12:24:23 +0000</pubDate>
      <link>https://dev.to/jcolag/small-d-date-night-46dd</link>
      <guid>https://dev.to/jcolag/small-d-date-night-46dd</guid>
      <description>&lt;p&gt;This is the software kind of date—the kind at a UNIX/Linux command line—not the kind that requires you to break your social-distancing protocols…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Vcb_OYC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a9wdxrvjadr95n0up5ne.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Vcb_OYC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/a9wdxrvjadr95n0up5ne.jpg" alt="Calendar" title="Calendar"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://john.colagioia.net/blog/2020/03/16/dev-ides.html"&gt;Recently&lt;/a&gt;, I decided that I needed to pre-generate skeletal posts for an ongoing project on my personal blog.  On the one hand, I’m interested in the overall result of seeing how many weeks I've committed to staring me in the face.  But on the other—more relevant to an audience of fellow developers—the process of generating a list of dates in a shell script turned out to be interesting and satisfying.&lt;/p&gt;

&lt;p&gt;Unsurprisingly, this centers on the GNU &lt;a href="https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html"&gt;&lt;code&gt;date&lt;/code&gt;&lt;/a&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Another Day, Another Time
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;date&lt;/code&gt; documentation makes it clear enough that there is a &lt;code&gt;--date&lt;/code&gt; (or &lt;code&gt;-d&lt;/code&gt;) option that allows specifying a date, but what’s not made clear is how flexible this specifier can be. Any of the following work.  Deep breath!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A text-based date string, such as “18 Mar 2020” or “18 March 2020”&lt;/li&gt;
&lt;li&gt;A numerical date string, such “01/02/03” (Day/Month/Year) or “01-02-03” (Year-Month-Day) or even “20200314 1440” (YYYYMMDD HHMM); years from 00–68 are in the 21&lt;sup&gt;st&lt;/sup&gt; century (2000–2068) and 69–99 are in the 19&lt;sup&gt;th&lt;/sup&gt; century, &lt;strong&gt;but&lt;/strong&gt; 1–9 (no leading zero) are literal years CE 1–9&lt;/li&gt;
&lt;li&gt;A time, whether on a twelve-hour (“10:35 p.m.”) or twenty-four-hour (“22:35”) clock, optionally including a time-zone specifier by name (“GMT” or “PDT”)&lt;/li&gt;
&lt;li&gt;Complete timestamps (“2020-03-14T17:23:53.000000+0700”)&lt;/li&gt;
&lt;li&gt;Upcoming days of the week (“Thursday”)&lt;/li&gt;
&lt;li&gt;Space-separated relative specifiers (“last Thursday” or “2 years ago” or “last Monday +3 years +7 hours -21 minutes” or “now”)&lt;/li&gt;
&lt;li&gt;Seconds since the &lt;a href="https://en.wikipedia.org/wiki/Unix_time"&gt;UNIX Epoch&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Any of the above prefixed with a long-form timezone like “TZ=’Europe/Geneva’”&lt;/li&gt;
&lt;li&gt;A lot of these can be mixed, such as a numerical date string followed by relative specifiers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of those are useful, but obvious and uninteresting. However, those relative specifiers? That’s the stuff of programming! With relative specifiers, we can do something like this somewhat-contrived example.&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="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="k"&gt;for &lt;/span&gt;filename &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;--date&lt;/span&gt; &lt;span class="s2"&gt;"thursday +&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; days"&lt;/span&gt;
  &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This takes each file in the current directory (not for any useful reason, just because I wanted a finite set) and prints the date of every Thursday going forward by counting up in increments of seven days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Harvesting Information
&lt;/h2&gt;

&lt;p&gt;As far as it goes, the loop will serve us well…except that, if you’re familiar with &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; (which is what I use to run my blog), you probably know that I need a couple of different kinds of dates. The post filenames are in a “YYYY-MM-DD-title.md” format (the file for my original post on this is named &lt;code&gt;2020-03-18-date.md&lt;/code&gt;, for example), and the front-matter of the post includes a date and time, where the date can be a wide variety of formats.&lt;/p&gt;

&lt;p&gt;This brings us to &lt;em&gt;formatting&lt;/em&gt;. The format needed for the file name’s date is easy. In fact, there’s an in-built formatter for dash-delimited values in descending order of significance.&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="nb"&gt;date&lt;/span&gt; +%F
&lt;span class="c"&gt;# outputs "2020-03-14"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another interesting (if not relevant to where this conversation is going) format gives us UNIX time in seconds.&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="nb"&gt;date&lt;/span&gt; +%s
&lt;span class="c"&gt;# outputs "1584534083"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can prepend an &lt;code&gt;@&lt;/code&gt; to that output and use it to set the time using &lt;code&gt;--date&lt;/code&gt;, as above.  I also have some scripts that subtract two times in this format and feed the difference into &lt;code&gt;date&lt;/code&gt; to get minutes and seconds.&lt;/p&gt;

&lt;p&gt;What I decided to do with the pre-generated posts was to give them random release times. I actually publish the &lt;em&gt;Star Trek&lt;/em&gt; posts whenever I have a few free minutes on Thursday evenings, so the actual time doesn’t really matter and I didn’t want to convince myself that there was some “official” time. So, I generate random numbers for the minutes and seconds after 5 P.M.; I then assemble the time in the post’s front-matter.&lt;/p&gt;

&lt;p&gt;…Except that doing this isn’t aware of &lt;a href="https://en.wikipedia.org/wiki/Daylight_saving_time"&gt;daylight saving time&lt;/a&gt;. So, if I generate &lt;code&gt;2020-03-19 17:06:15-0400&lt;/code&gt; for my post tomorrow, that’s fine, because I’m currently on Eastern Daylight Time and will be tomorrow, too. But &lt;code&gt;2020-12-19 17:06:15-0400&lt;/code&gt; is supposed to be on Eastern Standard Time, so the displayed time is “04:06:15 PM EST.” And if I leave that specifier off, Jekyll assumes that I must mean UTC.&lt;/p&gt;

&lt;p&gt;I know, I know, daylight saving is stupid. Time zones are stupid. We should all probably just use UNIX time or &lt;a href="https://en.wikipedia.org/wiki/Swatch_Internet_Time"&gt;Swatch Internet Time&lt;/a&gt; or something…&lt;/p&gt;

&lt;p&gt;Anyway, one way that we can solve this new problem is by requesting the time zone’s offset on the relevant day, instead of filling it in manually.&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="nb"&gt;date&lt;/span&gt; +%z &lt;span class="nt"&gt;--date&lt;/span&gt; &lt;span class="s2"&gt;"thursday +343 days"&lt;/span&gt;
&lt;span class="c"&gt;# outputs "-0500" for me&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt; +%z
&lt;span class="c"&gt;# outputs "-0400"&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt; +%F,%z
&lt;span class="c"&gt;# outputs "2020-03-18,-0400"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can then parse that time offset and append to my generated time to get most of what I want.&lt;/p&gt;

&lt;p&gt;…Except that I didn’t actually &lt;em&gt;use&lt;/em&gt; &lt;code&gt;Thursday +343 days&lt;/code&gt; in my script, since I only learned that combining mixed kinds of specifiers was an option later in the process. Rather than specifying “Thursday,” I stupidly specified the date of the first Thursday that I wanted using the &lt;code&gt;faketime&lt;/code&gt; utility, which came with an explicit time of midnight. So as I crossed daylight saving thresholds, the output times would bump back and forth by an hour, which meant being one day off (standard time shows as an hour behind, so midnight becomes 11 P.M. the previous night) for part of the year.&lt;/p&gt;

&lt;p&gt;I could have solved this by doing better research on the aforementioned &lt;code&gt;--date&lt;/code&gt; option, but I didn’t, and so the solution I came up with was to add another eight hours and request &lt;em&gt;that&lt;/em&gt; date (&lt;code&gt;%F&lt;/code&gt;) and time offset (&lt;code&gt;%z&lt;/code&gt;).  That's close enough for a quick hack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Have a Good Time…
&lt;/h2&gt;

&lt;p&gt;There’s obviously a little bit more to the script, like using a &lt;a href="https://en.wikipedia.org/wiki/Here_document"&gt;here document&lt;/a&gt; as a template for each file and creating the abbreviated titles for the filenames, but that’s all superfluous for this discussion, which is probably more than anybody could realistically want to know about generating a list of times.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Credits&lt;/strong&gt; : The header image is &lt;a href="https://pxhere.com/en/photo/12446"&gt;untitled calendar&lt;/a&gt; by an unknown photographer from &lt;a href="https://pxhere.com"&gt;PxHere&lt;/a&gt;, made available under the &lt;a href="https://creativecommons.org/publicdomain/zero/1.0/"&gt;CC0 1.0 Universal Public Domain Dedication&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>shell</category>
      <category>date</category>
    </item>
  </channel>
</rss>
