<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>blog.narjo.dev</title>
    <link>/</link>
    <description>Recent content on blog.narjo.dev</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Fri, 08 May 2026 16:41:33 -0700</lastBuildDate>
    <atom:link href="/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>My First Firefox Patch</title>
      <link>/posts/my-first-firefox-patch/</link>
      <pubDate>Fri, 08 May 2026 16:41:33 -0700</pubDate>
      <guid>/posts/my-first-firefox-patch/</guid>
      <description>The first Firefox bug I worked on: gaining context, writing a solution, and some tips.</description>
      <content:encoded><![CDATA[<p>This post continues the story I began in <a href="/posts/i-contributed-to-firefox">I contributed to Firefox</a>. Here I will discuss the first bug I worked on, which became the first patch I landed.</p>
<h2 id="the-bug">The Bug</h2>
<p>The first bug I took on was <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1895394">Bug 1895394 - Remove dom.event.wheel-event-groups.enabled pref</a>.</p>
<p>The bug contained sparse instructions. There was the title, &ldquo;Remove dom.event.wheel-event-groups.enabled pref&rdquo; and a single comment from the bug reporter:</p>
<blockquote>
<p>It&rsquo;s been enabled by default for about a year now (bug 1823700). Let&rsquo;s remove this feature rollout pref</p>
</blockquote>
<p>At this point I didn&rsquo;t know what a pref was or what it would take to remove it.</p>
<h2 id="finding-and-understanding-the-relevant-code">Finding and understanding the relevant code</h2>
<h3 id="initial-leads">Initial Leads</h3>
<p>There were a couple leads to follow: the pref name and the bug mentioned in the comment quoted above.</p>
<p>The text <code>dom.event.wheel-event-groups.enabled</code> in the title looked like code that might match exact text in a search of the codebase.</p>
<p>The bug mentioned in the comment was for enabling the pref by default in the main release rather than only on the nightly developer release. It was resolved by a patch changing a single line, in a file named <code>modules/libpref/init/StaticPrefList.yaml</code>.</p>
<p>Near the modified line, I found that string, <code>dom.event.wheel-event-groups.enabled</code>. Evidently, this was where the pref was defined and given a default value, <code>true</code> in this case.</p>
<p>Text search for the above string yielded results in other files. These included test configurations where the pref was forced to a certain value for test execution. It also occurred in comments across several files, in the context of stating that something would be the case if the pref was on or off.</p>
<p>Confusingly, this initial search did not yield any location where the pref was being read. So how was its value being used?</p>
<h3 id="moving-beyond-exact-text-search">Moving beyond exact text search</h3>
<p>The code matching an exact text search did not reveal how the pref was being read. To figure that out, I had to search for context in the surrounding code at a more zoomed out scale.</p>
<p><code>StaticPrefList.yaml</code> is a very long file, but at the top it does contain some documentation in the form of explanatory comments. This yielded an answer, explaining getter functions that can be made available for prefs:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># The getter function&#39;s base name is the same as the pref&#39;s name, but with
</span></span><span style="display:flex;"><span># &#39;.&#39; or &#39;-&#39; chars converted to &#39;_&#39;, to make a valid identifier. For example,
</span></span><span style="display:flex;"><span># the getter for `foo.bar_baz` is `foo_bar_baz()`. This is ugly but clear,
</span></span><span style="display:flex;"><span># and you can search for both the pref name and the getter using the regexp
</span></span><span style="display:flex;"><span># /foo.bar.baz/.
</span></span></code></pre></div><p>Aha! A search using the above regex formatting, <code>dom.event.wheel.event.groups.enabled</code> (<code>-</code> replaced with <code>.</code>) yielded what I was missing: the sites where the pref is accessed.</p>
<h3 id="zooming-out-again-to-find-documentation">Zooming out again to find documentation</h3>
<p>The bug description made it clear that the pref I was removing was a feature toggle. That was enough to work on the bug, but I was itching for a clearer general understanding of what a pref could be.</p>
<p>The comments at the top of <code>StaticPrefList.yaml</code> don&rsquo;t really explain what a pref is in general, speaking of the specifics of <em>static</em> prefs. However, the presence of those detailed comments spoke to the level of documentation embedded in the Firefox code.</p>
<p>Hoping to find something to explain prefs in general, I poked around up the directory tree and found that the <code>libpref</code> module contained a folder named <code>docs</code> with a single <code>index.md</code>. Bingo!</p>
<p>That document began by summarising that prefs can be used as feature toggles, preferences, debugging flags, and more.</p>
<p>With an understanding of generally what prefs are, and specifically where the one at issue was defined and used, I was ready to begin a solution.</p>
<h2 id="removing-the-pref">Removing the pref</h2>
<p>Removing the pref involved a few tasks:</p>
<ul>
<li>removing its definition</li>
<li>removing value overrides (as in test configs)</li>
<li>removing any mentions of the pref in comments</li>
<li>refactoring consumers to behave properly without accessing the pref value</li>
</ul>
<h3 id="most-changes-were-simple">Most changes were simple</h3>
<p>Removing the definition was as simple as deleting that block of code.</p>
<p>In all cases where the pref was being forced to a value for a test, it was being set to true. Since this was becoming not just the default but the only behavior, these tests would continue to work the same way with the pref removed, so I was able to simply delete the code setting the pref to true for these tests.</p>
<p>Comments mentioning the pref would no longer make sense once it was removed. This required individual attention to each context to ensure the comments made sense and were properly formatted after mention of the pref was removed.</p>
<h3 id="refactoring-consumers">Refactoring consumers</h3>
<p>Deleting use of the pref required some attention that the remaining code was left in the appropriate state given what you are trying to make permanent.</p>
<p>I&rsquo;ll give an example in pseudocode. Let&rsquo;s say this is where a pref is accessed:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">some_pref</span>() <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">other_condition</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// do stuff
</span></span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If <code>some_pref</code> has been defaulted to true and this is being made permanent, the conditional simplifies to <code>true &amp;&amp; other_condition</code>, which can be left as simply <code>other_condition</code>. if, however, it should be false, the conditional simplifies to <code>false &amp;&amp; ...</code> which will never be true, and you would want to delete this entire conditional branch as it could never happen!</p>
<p>This pref was effectively being set true permanently, so I was able to take the former approach, effectively replacing any usage with <code>true</code> and simplifying accordingly.</p>
<p>In my patch I changed this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">StaticPrefs</span><span style="color:#f92672">::</span><span style="color:#a6e22e">dom_event_wheel_event_groups_enabled</span>() <span style="color:#f92672">&amp;&amp;</span>
</span></span><span style="display:flex;"><span>    (<span style="color:#a6e22e">wheelEvent</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">mDeltaX</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0.0</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">wheelEvent</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">mDeltaY</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0.0</span>)) {
</span></span></code></pre></div><p>To this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">wheelEvent</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">mDeltaX</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0.0</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">wheelEvent</span><span style="color:#f92672">-&gt;</span><span style="color:#a6e22e">mDeltaY</span> <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0.0</span>) {
</span></span></code></pre></div><h2 id="finalizing-the-patch">Finalizing the patch</h2>
<p>I locally ran affected tests to verify they continued to pass. After checking with the bug reporter if I should run any other tests, I submitted the patch for review and it was approved. After spending a month in the beta, it shipped to Release channel Firefox with <a href="https://www.firefox.com/en-US/firefox/150.0/releasenotes/">Firefox 150</a>.</p>
<h2 id="practices-and-strategies-that-helped-me">Practices and strategies that helped me</h2>
<p>I&rsquo;ll finish off by rambling about some things I did that seemed to be pretty helpful. These aided my first contributions to Firefox, but none are specific to that context.</p>
<h3 id="creatively-explore-the-context">Creatively explore the context</h3>
<p>You can get a lot of mileage out of simple text search, especially with some creative use of regex or partial strings (e.g. <code>wheel.event.groups</code> instead of the whole thing). Search for file names, search within files&hellip; Perhaps use a bespoke search tool such as Searchfox if it exists for what you&rsquo;re working on.</p>
<p>Look for explanatory comments or documentation. Check the top of long files, check parent directories, check external documentation sites. Click through if your bug mentions another bug, to see if it offers helpful context. (Reminder: In Firefox development, &ldquo;bug&rdquo; is semi-synonymous with &ldquo;issue&rdquo; or &ldquo;ticket&rdquo;.)</p>
<p>Coding agents such as Claude Code can be a helpful option for a fuzzy search of a codebase, especially if you aren&rsquo;t sure what to run exact text search for. Usual cautions apply: double check conclusions, etc.</p>
<h3 id="take-notes-as-you-get-up-to-speed">Take notes as you get up to speed</h3>
<p>I take notes in markdown, linking related files with <code>[[Wikilinks]]</code>. I&rsquo;ve heard good things about Obsidian. For accessibility reasons I use VSCode, with an extension project called <a href="https://foamnotes.com/">Foam</a>.</p>
<p>Save relevant links: to the bug page, related bugs, documentation&hellip;</p>
<p>Paste in a list of the files you modified, and use that to help figure out what tests you should be running. Save a code block with the command you need to run your tests. Record which tests are passing or not.</p>
<p>Explain in your own words what&rsquo;s going on.</p>
<p>Draft questions to ask a bug mentor or other developer, and record key parts of their answers.</p>
<p>I took copious notes. They were helpful to remember what I had tried, what I had figured out or was still unsure about, and where key information was. They also made writing these posts much easier.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>This was a good first bug. As I discuss at the end of <a href="/posts/i-contributed-to-firefox">I Contributed to Firefox</a>, it&rsquo;s easier to get used to the development process if the particular issue you are solving isn&rsquo;t a huge lift.</p>
<p>I went on to land a couple more patches with similar scope, and as of writing I have a fourth patch in review which is quite different.</p>
<p>I have enjoyed my contributions to Firefox, and intend to make more.</p>
]]></content:encoded>
    </item>
    <item>
      <title>I Contributed to Firefox</title>
      <link>/posts/i-contributed-to-firefox/</link>
      <pubDate>Mon, 13 Apr 2026 08:00:00 -0700</pubDate>
      <guid>/posts/i-contributed-to-firefox/</guid>
      <description>Discussing my first contributions to Firefox Desktop, I introduce the tools, how to pick a bug, and some notes on my first patches.</description>
      <content:encoded><![CDATA[<p>I recently made my first contributions to Firefox Desktop.</p>
<p>I have landed three patches so far, resolving bugs <a href="https://github.com/mozilla-firefox/firefox/commit/61a79e2d2ccd">1895394</a>, <a href="https://github.com/mozilla-firefox/firefox/commit/1634f1927034">1899687</a>, and <a href="https://github.com/mozilla-firefox/firefox/commit/96ca091b925b">1950995</a>.</p>
<p>Beginning in this post, I&rsquo;ll write about some of the work I&rsquo;ve done and Firefox development generally.</p>
<p>This post introduces the tools of the trade, how to pick your first bug, and some brief notes on what my first contributions were.</p>
<p>I continue this story in <a href="/posts/my-first-firefox-patch">My First Firefox Patch</a>.</p>
<h2 id="the-tools">The tools</h2>
<p>The Firefox development ecosystem includes numerous tools, but some of the most important to understand are Bugzilla, Phabricator, Matrix, and Searchfox.</p>
<p><strong>Bugzilla</strong> is the issue tracking system, where bugs (issues) are reported, discussed, and closed.</p>
<p>When you are working on a bug and have code to contribute, either as a work in process or ready for review, you push it to <strong>Phabricator</strong>. On Phabricator, code changes are discussed, reviewed, and approved.</p>
<p>For more general communication, community communication channels are <strong>Matrix</strong> chat rooms. There is an <code>#introduction</code> channel ideal for new contributors&rsquo; questions.</p>
<p><strong>Searchfox</strong> is a bespoke search tool for the Firefox source code, useful both for finding things yourself and for linking specific lines of source code in discussions with other developers.</p>
<p>The Firefox source has a dedicated documentation site, including onboarding information for new contributors. See for instance <a href="https://firefox-source-docs.mozilla.org/setup/index.html">Getting Set Up To Work On The Firefox Codebase</a>.</p>
<h2 id="pick-a-good-first-bug">Pick a &ldquo;good first bug&rdquo;</h2>
<p>I will describe some basics of how to make your first contribution to Firefox. If you wish to contribute, refer to the Firefox source docs for thorough and up to date instructions.</p>
<p>Contribution starts with setting up your development environment. Use the bootstrap script to download the source code and install dependencies, confirm you can build and run it, and debug any issues that crop up.</p>
<p>To make your first contribution, you will probably want to pick an existing bug that has been described and categorized as a good first bug. You can filter on Bugzilla for the keyword <code>good-first-bug</code>, or browse Mozilla&rsquo;s <a href="https://codetribute.mozilla.org/">Codetribute</a>, a site for finding your first bug.</p>
<p>One benefit of Firefox development happening out in the open is that you can dig into the source code and start thinking about a bug on your own without having to ask permission or make any commitments. Hopefully, a look around the bug description and relevant source code will give you a sense of whether it is a problem you want to tackle.</p>
<p>You can post on Bugzilla indicating that you are planning to submit a patch for the bug, thereby saving someone else the trouble of wasting time doing parallel work, but you can also wait until you have a solution, and then submit a patch.</p>
<p>As you work on a bug, there&rsquo;s a good chance you will have questions, whether about general development processes or the particular bug. If there is a mentor assigned to the bug on Bugzilla, that&rsquo;s a great person to ask questions about the particular bug. You can also ask in Matrix.</p>
<h2 id="my-first-contributions-were-cleanup">My first contributions were cleanup</h2>
<p>The name &ldquo;bug&rdquo; may lead you to think that the issues tracked on Bugzilla are things that are broken or missing. They can be, but there are also tasks aimed at improving code that already works. You may even end up mostly deleting code, as I did in my first contributions.</p>
<p>The first three patches I landed all related to removing &ldquo;prefs&rdquo; that were no longer needed or wanted. A pref is a setting, such as for a user preference or a feature toggle. There are a lot of them, and some of them aren&rsquo;t necessary anymore. This excess is enough of an issue that there is a <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1773039">meta bug</a> collecting bugs about removing old prefs.</p>
<p>Deleting code may sound easy, and these were reasonably accessible bugs to start out with, but it does require some care to make sure that what you leave behind is correct. Even when taking a feature toggle that has been defaulted to on for years, and removing the prefs that allows it to be turned off, you may find it cropping up across the codebase in ways that can&rsquo;t simply be deleted.</p>
<p>In future posts I will discuss some of the complications I ran into on these bugs.</p>
<h2 id="contributing-is-its-own-problem">Contributing is its own problem</h2>
<p>I recommend deliberately starting with bugs that are easy enough that solving the programming problem of the bug is not a major obstacle. The development process is easier to learn if you aren&rsquo;t also trying to solve a hard programming problem. Knock out some easy bugs while learning how to submit code and communicate throughout the development life cycle.</p>
<p>My first contributions to Firefox did not have earthshattering impact on the browser, but they made me ready to tackle harder problems.</p>
<p>The followup post <a href="/posts/my-first-firefox-patch">My First Firefox Patch</a> discusses the first bug I completed, telling the story of how I got my wits about me and sharing some of the lessons I&rsquo;ve learned so far.</p>
]]></content:encoded>
    </item>
    <item>
      <title>A Bookstore in Ruby</title>
      <link>/posts/a-bookstore-in-ruby/</link>
      <pubDate>Fri, 28 Jun 2024 00:00:00 +0000</pubDate>
      <guid>/posts/a-bookstore-in-ruby/</guid>
      <description>Integrating Object Oriented Concepts with the Unified Model Strategy</description>
      <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>This post is about a study strategy and how that strategy relates to a specific course within the Core Curriculum of <a href="https://launchschool.com/">Launch School</a>. The course in question is Object Oriented Programming (OOP), denoted in the Ruby track as RB120 (lessons) and RB129 (assessments).</p>
<p>This post is the product of a collaboration with fellow student Wook Kim, and its backstory begins with an insight he had after completing his RB129 assessments, especially the interview.</p>
<p>Wook felt something was missing in his preparation that would have solidified his understanding and fluency, but he had an idea of what would have helped. He eagerly described his proposal and urged me to try it as I prepared for the same assessments.</p>
<p>Having passed both assessments after putting into practice Wook&rsquo;s idea, I can strongly recommend it.</p>
<h2 id="wooks-insight">Wook&rsquo;s Insight</h2>
<p>Wook described two struggles.</p>
<p>First, he felt that while he understood individual concepts, he didn&rsquo;t have a comprehensive mental model to tie everything together.</p>
<p>Second, when asked to provide examples, he had to come up with them on the fly. His suggested study strategy would address both points.</p>
<p>The technique he proposed was to pick a real world domain I was familiar with and model it in Ruby using the OOP concepts from the course. Specifically, his idea was to create a single model covering all the concepts in one place. I&rsquo;ll call this the &ldquo;unified model.&rdquo;</p>
<p>I will attempt to detail a &ldquo;recipe&rdquo; for this strategy, but I improvised every step of the way. It&rsquo;s <em>plausible</em> that I didn&rsquo;t make <em>every</em> best choice.</p>
<p>Furthermore, when I returned to Wook during my studies to clarify the specifications for an end product, he instead encouraged me to focus on &ldquo;the why.&rdquo; In this case, that meant focusing on building understanding rather than achieving a particular end product.</p>
<p>The point is not to write a Ruby file fulfilling a certain specification; the point is an integrated understanding of OOP concepts. If the path you take creates understanding, mission accomplished.</p>
<h2 id="part-of-a-balanced-breakfast">&ldquo;Part of a Balanced Breakfast&rdquo;</h2>
<p>This was far from the only strategy I employed while studying for these assessments.</p>
<p>I can highly recommend this technique <em>together with</em> peer study sessions, TA sessions, and good old-fashioned curriculum review.</p>
<p>I err on the side of over-preparing for Launch School assessments. These were no exception, but they were my most comfortable assessment experiences yet. The unified model strategy was key in bringing me from tentative understanding to fluency and quick recall.</p>
<h2 id="the-recipe">The Recipe</h2>
<p>Think of my detailed instructions as more a record of my improvisation than dogma you must follow.</p>
<ol>
<li>Domain: Choose something familiar. Workplaces, sports teams, and other human organizations are great candidates. Pick something where a general audience will be able to understand your snippets without extra context.</li>
<li>Individual snippets: In the context of your chosen domain, give a code example to illustrate XYZ concept. Can you model the positions on a team using class inheritance? How does encapsulation relate to a customer&rsquo;s experience in a restaurant? What kind of collaborator objects would a <code>PlayerCharacter</code> class have in a roleplaying game?</li>
<li>Refine: Make your snippets better! I elaborate on this later. With many of these concepts, we are in the fuzzy zone of making design decisions with tradeoffs. Try things out!</li>
<li>Unify: Start with a checklist of the concepts you want to make sure your unified model illustrates. Then do it all in one big (hopefully not too big!) Ruby file. Refine further!</li>
<li>Practice: <strong>With little to no reference to your existing snippets,</strong> quiz yourself by writing from scratch code illustrations of different concepts from the course.</li>
</ol>
<h2 id="why-metaphor--what-it-is-were-doing-here">Why Metaphor? / What It Is We&rsquo;re Doing Here</h2>
<h3 id="the-value-of-the-concrete">The Value of the Concrete</h3>
<p>We deliberately use nontechnical nouns and verbs as the subjects of our metaphors when developing mental models.</p>
<p>The Launch School curriculum frequently illustrates object oriented concepts through examples such as types of vehicles or animals.</p>
<p>These concrete things make for much better illustrations than abstract data types because we can jump-start our understanding of code relationships by piggybacking on our existing understanding of relationships in the real world.</p>
<h3 id="piggyback-on-existing-integration">Piggyback on Existing Integration</h3>
<p>Setting your illustrations of different OOP concepts in the same domain makes them easier to integrate.</p>
<p>Your &ldquo;assignment&rdquo; is not just to write code so much as to develop a library of connected metaphors to fluently draw from when called upon to explain concepts. We develop the metaphor to integrate our mental models, and we write corresponding Ruby to develop integrated examples.</p>
<p>Finally, in case it is not already clear, we are not writing a program here. These are more like mental maps or diagrams in code form.</p>
<h2 id="putting-it-into-practice">Putting It Into Practice</h2>
<p>When I first began reviewing OOP concepts, the prospect of illustrating them all in a unified model was daunting. Before writing the unified model, I alternated reviewing course materials with writing individual snippets to illustrate specific concepts.</p>
<p>I started with the concepts I was most confident with. Class inheritance and module mixins?&ndash;no problem. Constant scope?&ndash;let me get back to you. This brings me to a clarifying question:</p>
<p><strong>Q:</strong> When should I start doing this?<br>
<strong>A:</strong> Once you have completed the lessons and seen the study guide, you may be comfortable enough with some concepts to write illustrative examples of them. Later, combine concepts into larger illustrations, and eventually a unified model. In other words, this is a review strategy. Do it when you&rsquo;re reviewing!</p>
<h2 id="my-particulars">My Particulars</h2>
<p>For a domain I picked a bookstore, and began by writing individual snippets to illustrate the concepts.</p>
<p>Attempting to write the snippets revealed shortcomings in my understanding that I addressed mostly through rereading the course material. Refining the snippets brought fluency and clarity. I wrote some intermediate snippets that combined several concepts, and eventually wrote the suggested unified model that utilized all the key concepts from the course.</p>
<p>Coming up with your own code illustrations and model is the helpful bit, so I won&rsquo;t be providing my final unified model for your reference. However, I will give smaller examples, both to paint a broad picture of what I did and to illustrate some of the lessons I learned in revising.</p>
<h3 id="the-bookstore-metaphor">The Bookstore Metaphor</h3>
<p>So, how did I model object oriented programming concepts in the domain of a bookstore?</p>
<ul>
<li>Class inheritance: types of employees (manager, bookseller, owner&hellip;)</li>
<li>Polymorphism: unrelated product types that all get treated the same at the cash register</li>
<li>Collaborator objects: an instance of <code>Inventory</code> stores instances of <code>Book</code></li>
<li>Protected methods: <code>Employee</code> instances can access certain methods such as a work schedule from other employees, but those methods are off limits to a <code>Customer</code></li>
</ul>
<p>Hopefully you get the idea.</p>
<h2 id="creating-effective-illustrations">Creating Effective Illustrations</h2>
<p>I revised many of my snippets. Some improvements were immediately obvious, but others came after going to TA sessions and SPOT sessions (Launch School&rsquo;s peer-lead study groups) and getting feedback on the illustrations I provided.</p>
<p>I&rsquo;ll demonstrate some rules of thumb that may improve your illustrations.</p>
<h3 id="simplify">Simplify</h3>
<p>You may feel the urge to write code examples that are more fully featured, that look more like a &ldquo;real program.&rdquo; This impulse can add unnecessary and distracting detail.</p>
<p>Here I&rsquo;ll share two snippets: one that has some unnecessary complexity and then a simplified version.</p>
<p>I wrote the following to illustrate collaborator objects and private getters:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Inventory</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">initialize</span>
</span></span><span style="display:flex;"><span>    @stock <span style="color:#f92672">=</span> <span style="color:#66d9ef">Hash</span><span style="color:#f92672">.</span>new(<span style="color:#ae81ff">0</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">[]</span>(book)
</span></span><span style="display:flex;"><span>    stock<span style="color:#f92672">[</span>book<span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">[]=</span>(book, quantity)
</span></span><span style="display:flex;"><span>    stock<span style="color:#f92672">[</span>book<span style="color:#f92672">]</span> <span style="color:#f92672">=</span> quantity
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">all_titles</span>
</span></span><span style="display:flex;"><span>    puts stock<span style="color:#f92672">.</span>keys<span style="color:#f92672">.</span>map(<span style="color:#f92672">&amp;</span><span style="color:#e6db74">:title</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">private</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">attr_reader</span> <span style="color:#e6db74">:stock</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Book</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">attr_reader</span> <span style="color:#e6db74">:title</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">initialize</span>(title)
</span></span><span style="display:flex;"><span>    @title <span style="color:#f92672">=</span> title
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>white_fang <span style="color:#f92672">=</span> <span style="color:#66d9ef">Book</span><span style="color:#f92672">.</span>new(<span style="color:#e6db74">&#39;White Fang&#39;</span>)
</span></span><span style="display:flex;"><span>piranesi <span style="color:#f92672">=</span> <span style="color:#66d9ef">Book</span><span style="color:#f92672">.</span>new(<span style="color:#e6db74">&#39;Piranesi&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>store <span style="color:#f92672">=</span> <span style="color:#66d9ef">Inventory</span><span style="color:#f92672">.</span>new
</span></span><span style="display:flex;"><span>p store<span style="color:#f92672">[</span>white_fang<span style="color:#f92672">]</span> <span style="color:#75715e"># 0</span>
</span></span><span style="display:flex;"><span>store<span style="color:#f92672">.</span>all_titles <span style="color:#75715e"># no output</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>store<span style="color:#f92672">[</span>piranesi<span style="color:#f92672">]</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">3</span>
</span></span><span style="display:flex;"><span>store<span style="color:#f92672">[</span>piranesi<span style="color:#f92672">]</span> <span style="color:#f92672">-=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>p store<span style="color:#f92672">[</span>piranesi<span style="color:#f92672">]</span> <span style="color:#75715e"># 2</span>
</span></span><span style="display:flex;"><span>store<span style="color:#f92672">.</span>all_titles <span style="color:#75715e"># Piranesi</span>
</span></span></code></pre></div><p>Upon further reflection, I realized that the above snippet can be cleaned up a bit. Using a hash introduces complexity that doesn&rsquo;t further the concept I&rsquo;m trying to illustrate. Also, instantiating two book objects serves no purpose here; one would do just fine.</p>
<p>Below is a slightly simplified snippet. It uses a simple array rather than a hash. Also, the <code>#in_stock?</code> method is a clearer example (than <code>#all_titles</code>) of a public method that responds based on private state that is not exposed.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Inventory</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">initialize</span>
</span></span><span style="display:flex;"><span>   @stock <span style="color:#f92672">=</span> <span style="color:#f92672">[]</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">in_stock?</span>(title)
</span></span><span style="display:flex;"><span>   stock<span style="color:#f92672">.</span>any? { <span style="color:#f92672">|</span>book<span style="color:#f92672">|</span> book<span style="color:#f92672">.</span>title <span style="color:#f92672">==</span> title }
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">receive</span>(book)
</span></span><span style="display:flex;"><span>   stock <span style="color:#f92672">&lt;&lt;</span> book
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">attr_reader</span> <span style="color:#e6db74">:stock</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Book</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">attr_reader</span> <span style="color:#e6db74">:title</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">initialize</span>(title)
</span></span><span style="display:flex;"><span>   @title <span style="color:#f92672">=</span> title
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>inventory <span style="color:#f92672">=</span> <span style="color:#66d9ef">Inventory</span><span style="color:#f92672">.</span>new
</span></span><span style="display:flex;"><span>p inventory<span style="color:#f92672">.</span>in_stock?(<span style="color:#e6db74">&#39;Piranesi&#39;</span>) <span style="color:#75715e"># false</span>
</span></span><span style="display:flex;"><span>inventory<span style="color:#f92672">.</span>receive(<span style="color:#66d9ef">Book</span><span style="color:#f92672">.</span>new(<span style="color:#e6db74">&#39;Piranesi&#39;</span>))
</span></span><span style="display:flex;"><span>p inventory<span style="color:#f92672">.</span>in_stock?(<span style="color:#e6db74">&#39;Piranesi&#39;</span>) <span style="color:#75715e"># true</span>
</span></span><span style="display:flex;"><span>inventory<span style="color:#f92672">.</span>stock <span style="color:#75715e"># error</span>
</span></span></code></pre></div><p><strong>Every bit of simplicity counts.</strong> When you are taking an assessment, half of your brain might be in panic mode. You want to build up a library of examples that you won&rsquo;t get tripped up trying to reproduce. Also, when you eventually unify your snippets, compact and efficient examples will be easier to integrate.</p>
<p>To simplify a snippet, ask yourself questions like:</p>
<ul>
<li>Can I illustrate this with fewer methods?</li>
<li>Does this collection need this many entries? (Your example array probably doesn&rsquo;t need ten elements. Three will do.)</li>
<li>Can I use a simpler data structure?</li>
<li>When I &ldquo;run some code&rdquo; at the end to demonstrate the concept, are my examples redundant? What&rsquo;s the simplest way to show how it works?</li>
</ul>
<p>Regarding that last point&hellip;</p>
<h3 id="run-some-code">Run Some Code</h3>
<p>Don&rsquo;t just write classes that illustrate the concepts at hand; run some code to show how it works!</p>
<p>The above snippets are primarily concerned with illustrating collaborator objects. As such, it&rsquo;s critical to include that last section where I populate the <code>Inventory</code> with a <code>Book</code> object and test the public interface. In some cases it may be useful to demonstrate code that throws an error, for instance to show where we can and can&rsquo;t invoke private methods.</p>
<p>When putting together your unified model, I recommend leaving out the demonstrative code-running. It clutters the file to demonstrate dozens of things.</p>
<h3 id="annotate-your-snippets">Annotate Your Snippets</h3>
<p>As your understanding improves and you review snippets you&rsquo;ve written, you may come to realize&hellip; well, all sorts of things! Leave comments in your snippet files to preserve your observations alongside the relevant code.</p>
<p>One genre of comment I left in several snippets was essentially, &ldquo;When would I want to use this?&rdquo;</p>
<p>For instance:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># class variables are useful for storing common state</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># ...tied to the class rather than specific instances</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># a basic example is incrementing the instance count on initialization</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Book</span>
</span></span><span style="display:flex;"><span>  @@books_on_record <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">self</span><span style="color:#f92672">.</span><span style="color:#a6e22e">how_many_exist</span>
</span></span><span style="display:flex;"><span>    @@books_on_record
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">initialize</span>
</span></span><span style="display:flex;"><span>    @@books_on_record <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">end</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">end</span>
</span></span></code></pre></div><p>Some things to consider reflecting on and annotating:</p>
<ul>
<li>When would I use this concept? What is a classic use case?</li>
<li>Does the real world situation I&rsquo;m modeling here feel like it would be better modeled with a different structure? (When illustrating polymorphism, you may come to realize the nouns you are using to illustrate duck typing would make more sense in a common class inheritance structure, or vice versa.)</li>
</ul>
<p>Your snippets may be saved in Ruby files, but they are part of your notes, and you can write whatever you want in them!</p>
<h3 id="contradict-yourself">Contradict Yourself</h3>
<p>I&rsquo;ve shared a couple implementations of a <code>Book</code> class that looked quite different. You may wonder if your snippets should play nice with each other and be non-contradictory, so that you can easily just combine them when you want to build a unified model. I say no!</p>
<p>When you are writing many snippets to illustrate a particular concept or a few concepts together, this is a great time to &ldquo;explore the space&rdquo; of your metaphor. Model the same noun in different ways, or use different nouns to model the same concept, and see what looks better.</p>
<p>The <code>Book</code> snippets I&rsquo;ve shared are not contradictory in the sense that you <em>could</em> combine them, but in each snippet I only included the detail necessary for the concept at hand. The same overall principle applies in that each snippet is an independent effort to illustrate something. Consistency comes later.</p>
<h2 id="the-unified-model">The Unified Model</h2>
<p>All this talk about snippets, but wasn&rsquo;t the whole point to build a unified model? When should you put it all together and what should it look like?</p>
<p>Let&rsquo;s refresh what we&rsquo;re trying to do here:</p>
<ul>
<li>We want to integrate our understanding of different concepts.</li>
<li>We want fluency with giving examples to illustrate concepts.</li>
</ul>
<p>And our strategy is:</p>
<ul>
<li>We develop an extensive mental model mapping object oriented programming concepts to a real world domain.</li>
<li>We use that metaphor as the setting for code illustrations of concepts.</li>
</ul>
<p>Truth be told, writing separate snippets that draw from the same context does a lot of work solidifying our understanding. You could stop there and have benefited greatly, but don&rsquo;t! Distilling that work into a unified model is quite worthwhile.</p>
<h3 id="why-bother-with-the-unified-model">Why Bother With the Unified Model?</h3>
<h4 id="goal-power">Goal Power</h4>
<p>One reason to plan on creating the unified model is simply the purpose it serves being in the back of your mind as a goal. It is a daunting task that acts as a litmus test for your confidence in the material, and it encourages taking intermediate steps of study to clarify and condense your understanding.</p>
<h4 id="resolving-contradictions">Resolving Contradictions</h4>
<p>Putting your examples for all the concepts in the same place encourages compact examples, but it also forces you to resolve any contradictory or confusing overlapping metaphors.</p>
<p>I encouraged experimentation within individual snippets, but when it comes to the unified model you need to &ldquo;make a choice&rdquo; about which nouns to tie to which concepts.</p>
<p>This is the final stage of refining the examples you will draw from when asked to explain concepts. You want to be able to illustrate all concepts without your examples stepping on each other&rsquo;s toes.</p>
<h4 id="an-aside-be-flexible">An Aside: Be Flexible</h4>
<p>The particular implementation you use to illustrate a concept in your unified model is not the only way to do things. You may very well do things differently on an assessment.</p>
<p>You will be more flexible if you have tried out similar implementations during the snippet phase.</p>
<h4 id="quick-reference">Quick Reference</h4>
<p>The desire for tidy examples brings me to the final use of the unified model: a quick reference study tool. If all the relevant concepts are illustrated in your unified model, you can skim it and ask yourself &ldquo;do I understand what&rsquo;s happening here?&rdquo; and rest assured you&rsquo;re covering everything. I&rsquo;m talking about jogging your memory, not achieving initial mastery.</p>
<h3 id="what-it-is-what-it-isnt">What It Is, What It Isn&rsquo;t</h3>
<p>To reiterate what I&rsquo;m suggesting the unified model be:</p>
<ul>
<li>The unified model&rsquo;s purpose is more like notes than a program.</li>
<li>The unified model illustrates all relevant concepts.</li>
<li>All illustrations in the unified model should be set in the same general context.</li>
<li>Different aspects of the unified model do not need to interact with each other in code.</li>
</ul>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<h3 id="good-enough">Good Enough</h3>
<p>When I created my &ldquo;unified&rdquo; model, the disconnectedness of different illustrations made me feel sort of like I had just put separate snippets in the same file. That&rsquo;s fine!</p>
<p>Having that file allowed me to go to one place to review the model I developed, and ensured that the particular illustrations I drilled late in the study process were not contradictory.</p>
<p>By that time I was starting to think &ldquo;I should just take the assessment already,&rdquo; so I wasn&rsquo;t overly concerned with spending a long time doing it just right.</p>
<p>This goes back to my point about how having the unified model as a goal leads you to some great review before you even get to the &ldquo;unified&rdquo; part.</p>
<h3 id="theyre-more-like-guidelines">&ldquo;They&rsquo;re More Like Guidelines&rdquo;</h3>
<p>Again, my detailed instructions are an attempt to be clear and helpful, rather than prescriptive.</p>
<p>The initial suggestion I followed left room for interpretation. What I improvised worked well for me and I wanted to share it. Take what makes sense to you and adapt it to your needs!</p>
<h3 id="reach-out-ill-be-there">Reach Out (I&rsquo;ll Be There)</h3>
<p>I&rsquo;m very curious to hear from other students about whether they&rsquo;ve done something similar and how it went. Feel free to reach out with your thoughts.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Coding With Talon Voice</title>
      <link>/posts/coding-with-talon-voice/</link>
      <pubDate>Fri, 03 Nov 2023 00:00:00 +0000</pubDate>
      <guid>/posts/coding-with-talon-voice/</guid>
      <description>How I control my computer and write code using Talon Voice</description>
      <content:encoded><![CDATA[<p>Due to chronic RSI, I&rsquo;m limited in how much I can use a mouse and keyboard. A few accessibility tools have allowed me to continue programming.</p>
<p>The backbone of my assisted computer input (and the topic of this post) is the voice command/dictation software <a href="https://talonvoice.com/">Talon Voice</a>. Talon builds out functionality with user scripts in Talon&rsquo;s command scripting language and python. Practically speaking, you can just clone the community maintained starter repo to get started.</p>
<h2 id="what-talon-looks-like">What Talon Looks Like</h2>
<p>It&rsquo;s tricky to pick the perfect demonstration of Talon. Personalization is a key feature and everyone&rsquo;s setup is a little different. Some of the best short and sweet video demonstrations are primarily demonstrations of the (amazing, game-changing) auxiliary tool Cursorless. There are some great conference talks about Talon that are years old and therefore partly outdated. I recommend approaching demonstrations with the aim to get an idea of the possibilities. If you choose to dive in, refer to current documentation.</p>
<p>Here are some demonstrations:</p>
<ul>
<li>
<p><a href="https://www.joshwcomeau.com/blog/hands-free-coding/">Josh W Comeau&rsquo;s great post on hands free coding</a> includes some demos and explanations of Talon basics. It is a few years old, written I believe before Cursorless existed.</p>
</li>
<li>
<p>Short and sweet 1 minute demo of writing some Java with Talon (<a href="https://www.youtube.com/watch?v=1mgFn6ifo_Y">YouTube</a>).</p>
</li>
<li>
<p>Here&rsquo;s a 2 minute demo (<a href="https://www.youtube.com/watch?v=0ZZb12Qp6-0">YouTube</a>) from the creator of Cursorless, using Talon with Cursorless.</p>
</li>
<li>
<p>Emily Shea&rsquo;s 40 minute conference talk (<a href="https://www.youtube.com/watch?v=YKuRkGkf5HU">YouTube</a>) from September 2019 shares her experience transitioning to code by voice using Talon. Includes a demo, discussion of voice coding pros and cons, tips, and what things are and aren&rsquo;t actually problematic. Keep in mind it&rsquo;s over 4 years old now. Some of the technical specifics are outdated, but it&rsquo;s a helpful glimpse into a development career enabled by voice coding.</p>
</li>
</ul>
<p>On that last note, I just want to underscore that there are professional developers whose careers would have ended without these tools. Dictation is a viable alternative to mouse and keyboard. Voice controlled computing has grown tremendously in the last decade, and I expect it will only improve.</p>
<h2 id="getting-started">Getting Started</h2>
<p>Want to give it a go? See the <a href="https://talon.wiki/">Talon Community Wiki</a>.</p>
<p>The starter repo is expansive and regularly updated. I continue to discover useful commands I didn&rsquo;t realize exist. Get to know the community repo and tweak things to your liking.</p>
<p>Most of the modifications I&rsquo;ve made have been keyword changes or simple commands to insert specific text. I haven&rsquo;t gotten that into the weeds but you certainly could if you want. Here&rsquo;s <a href="https://github.com/narjoDev/talon-community/commit/5bd1cec115ec360c015374d88e7e24ce06cb4b9f">a typical tweak commit</a> I made to my fork. You can get a lot of mileage out of simple tweaks that don&rsquo;t require understanding more complex concepts.</p>
<p>Beyond the starter repo, my top recommendations to extend Talon would be <a href="https://github.com/david-tejada/rango">Rango</a> for browser navigation and <a href="https://www.cursorless.org/">Cursorless</a> for programming and text editing.</p>
<h2 id="parting-advice">Parting Advice</h2>
<p>Expect some feelings of overwhelm. These are powerful tools and there&rsquo;s a lot to figure out. That said, I&rsquo;ll emphasize again you can get a lot of bang for your buck by getting comfortable with the basics and finding personalized solutions to your biggest friction points.</p>
<p>The Talon Slack (linked on the website) is a great place to get help if you are stuck. Standard advice on asking for help applies: do your best to figure things out by yourself first (read the manual!) and explain what you have tried.</p>
<p>Feel free to reach out to me with questions.</p>
]]></content:encoded>
    </item>
    <item>
      <title>About</title>
      <link>/about/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/about/</guid>
      <description>&lt;p&gt;My name is Nathan Johnson, and this is my blog. I&amp;rsquo;m a full stack software engineer and mostly write about that.&lt;/p&gt;
&lt;p&gt;For my brag sheet, please visit my home page, &lt;a href=&#34;https://narjo.dev/&#34;&gt;narjo.dev&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I use dictation for most computer input, such as &lt;a href=&#34;/posts/coding-with-talon-voice&#34;&gt;Coding with Talon Voice&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can subscribe to this blog via the &lt;a href=&#34;/index.xml&#34;&gt;RSS feed&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;contact&#34;&gt;Contact&lt;/h2&gt;
&lt;p&gt;You can contact me at &lt;a href=&#34;mailto:dev@narjo.dev&#34;&gt;dev@narjo.dev&lt;/a&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>My name is Nathan Johnson, and this is my blog. I&rsquo;m a full stack software engineer and mostly write about that.</p>
<p>For my brag sheet, please visit my home page, <a href="https://narjo.dev/">narjo.dev</a>.</p>
<p>I use dictation for most computer input, such as <a href="/posts/coding-with-talon-voice">Coding with Talon Voice</a>.</p>
<p>You can subscribe to this blog via the <a href="/index.xml">RSS feed</a>.</p>
<h2 id="contact">Contact</h2>
<p>You can contact me at <a href="mailto:dev@narjo.dev">dev@narjo.dev</a>.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
