<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Senior Engineering Architect on Ryan M. Poe</title>
    <link>https://ryanmpoe.com/</link>
    <description>Recent content in Senior Engineering Architect on Ryan M. Poe</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Sat, 03 Jan 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://ryanmpoe.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>A 64x64 Weather Display for a Divoom</title>
      <link>https://ryanmpoe.com/2026-01-03-weather-pixel-art/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2026-01-03-weather-pixel-art/</guid>
      <description>&lt;p&gt;I have a nifty &lt;a href=&#34;https://divoom.com/products/pixoo-64&#34;&gt;Divoom Pixoo-64&lt;/a&gt; on my desk that I got for Christmas a few years back. It&amp;rsquo;s a 64x64 LED display that can show pixel art, animations, the time, etc. But if you don&amp;rsquo;t mind code, it can show whatever you want. I wanted it to show the weather in a specific way that I liked.&lt;/p&gt;
&lt;figure class=&#34;l:float-right&#34;&gt;&lt;img src=&#34;./images/posts/divoom-web.jpg&#34;
    alt=&#34;Divoom Pixoo-64 displaying weather&#34;&gt;&lt;figcaption&gt;
      &lt;p&gt;The Divoom on my office wall&lt;/p&gt;
    &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The result: a little serverless function that generates a 64x64 pixel image with the current temperature, forecast, date, and time. It runs on Cloudflare Workers now, though it started as a Netlify function back in July 2023.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Engineering Year in Review Guide</title>
      <link>https://ryanmpoe.com/2026-01-02-year-in-review-guide/</link>
      <pubDate>Fri, 02 Jan 2026 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2026-01-02-year-in-review-guide/</guid>
      <description>&lt;p&gt;For our team&amp;rsquo;s final 2025 meeting, &lt;a href=&#34;./acme-2025&#34;&gt;I made a nice 2025 end-of-year report&lt;/a&gt; on our code entropy over the past year, with a focus on recognizing the contributions of our team. Sharing this guide for anyone who might want to do something similar.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s nice to have a snapshot of what a team accomplished in code form: code commits, PR reviews, cross-repo work, security patches, dependency updates - it all adds up to something worth celebrating.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Breakdance: Automating parts of our SDLC</title>
      <link>https://ryanmpoe.com/2025-12-29-breakdance/</link>
      <pubDate>Mon, 29 Dec 2025 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2025-12-29-breakdance/</guid>
      <description>&lt;p&gt;At WorkTango, a pull request (PR) moves through a predictable lifecycle:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Developer opens a PR against the main branch (&lt;code&gt;master&lt;/code&gt;, in our case)&lt;/li&gt;
&lt;li&gt;CI runs (tests, linting, type checking)&lt;/li&gt;
&lt;li&gt;At least one approving review required (more for certain files via &lt;code&gt;CODEOWNERS&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;All status checks must pass&lt;/li&gt;
&lt;li&gt;When ready, the developer adds a GitHub label (&lt;code&gt;breakdance&lt;/code&gt;) to request auto-merge&lt;/li&gt;
&lt;li&gt;On merge: commits are squashed into a single commit&lt;/li&gt;
&lt;li&gt;Commit title = PR title (must contain a Jira ticket, e.g., &lt;code&gt;WT-12345&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Commit description = extracted from the PR body&amp;rsquo;s &amp;ldquo;Commit Message&amp;rdquo; section&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps our git history clean: one commit per PR, meaningful messages, traceable to Jira.&lt;/p&gt;</description>
    </item>
    <item>
      <title>ESM and `__dirname`</title>
      <link>https://ryanmpoe.com/2024-07-14-esm-dirname/</link>
      <pubDate>Sun, 14 Jul 2024 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2024-07-14-esm-dirname/</guid>
      <description>&lt;p&gt;As a Node/TypeScript engineer constantly trying to prune build tools and layers of complexity away, I find ECMAscript modules quite the difficulty hurdle. A lot of the little rules and differences between ESM and CJS have deep technical reasons behind them, many of which I&amp;rsquo;m sure are difficult to explain.&lt;/p&gt;
&lt;p&gt;One of those is the whole &amp;ldquo;where&amp;rsquo;s my &lt;code&gt;__dirname&lt;/code&gt; and &lt;code&gt;__filename&lt;/code&gt;?!&amp;rdquo; kerfuffle. And it&amp;rsquo;s probably instructive.&lt;/p&gt;
&lt;p&gt;You can&amp;rsquo;t simply use &lt;code&gt;__dirname&lt;/code&gt; in ESM to get a filesystem directory. The standard was written for browsers, not file systems. Sure, there&amp;rsquo;s a &lt;em&gt;convention&lt;/em&gt; that overlaps file system semantics onto URLs. But there&amp;rsquo;s no guarantee that &lt;code&gt;https://website.com/my/path&lt;/code&gt; overlays onto &lt;code&gt;/var/www/my/path&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Testing as thinking</title>
      <link>https://ryanmpoe.com/2023-12-22-tdd-is-for-engineers/</link>
      <pubDate>Thu, 21 Dec 2023 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2023-12-22-tdd-is-for-engineers/</guid>
      <description>&lt;p&gt;I got to thinking about all of the &lt;a href=&#34;https://news.ycombinator.com/item?id=32712909&#34;&gt;ire&lt;/a&gt; &lt;a href=&#34;https://dhh.dk/2014/tdd-is-dead-long-live-testing.html&#34;&gt;I often&lt;/a&gt; &lt;a href=&#34;https://martinfowler.com/articles/is-tdd-dead/&#34;&gt;see directed&lt;/a&gt; &lt;a href=&#34;https://www.reddit.com/r/programming/comments/23rmdw/tdd_is_dead_long_live_testing_dhh/&#34;&gt;toward&lt;/a&gt; Test Driven Development (TDD). Developers tend to consider the &amp;ldquo;write the test first&amp;rdquo; dogma to be unhelpful&amp;ndash;too picky for people who actually want to ship products.&lt;/p&gt;
&lt;p&gt;I totally understand. Nobody likes something that exists only as a set of obtrusive, mechanical, seemingly regressive rules. (This is tilting at windmills, though. Nobody got fired for forgetting to write their test before their implementation.) I don&amp;rsquo;t want to even sell you on test-first engineering. I almost never do that!&lt;/p&gt;</description>
    </item>
    <item>
      <title>TypeScript types from a database connection</title>
      <link>https://ryanmpoe.com/2023-11-08-generate-types-from-db-schema/</link>
      <pubDate>Wed, 08 Nov 2023 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2023-11-08-generate-types-from-db-schema/</guid>
      <description>&lt;p&gt;There are a whole lot of NodeJS ORMs and/or DB management drivers to choose from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Objection&lt;/li&gt;
&lt;li&gt;Sequelize&lt;/li&gt;
&lt;li&gt;Knex&lt;/li&gt;
&lt;li&gt;Prisma&lt;/li&gt;
&lt;li&gt;Hasura&lt;/li&gt;
&lt;li&gt;Raw mode (PG2, node-mysql, SQLite, etc.)&lt;/li&gt;
&lt;li&gt;TypeORM&lt;/li&gt;
&lt;li&gt;Kysely&lt;/li&gt;
&lt;li&gt;Mongoose&lt;/li&gt;
&lt;li&gt;Waterline (Sails)&lt;/li&gt;
&lt;li&gt;MikroORM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;ve used most of them at some point, either professionally or in side projects. Over time, I&amp;rsquo;ve developed a preference &lt;em&gt;against&lt;/em&gt; treating my schema shape (in code) as the primary artifact of my database designs. You know what I mean: the &lt;code&gt;entities&lt;/code&gt; folder, with 30-75 files tucked in under it, each representing one or more database table as some sort of class, or object, usually crawling with decorators. Sometimes your ORM might actually write migrations from those entities.&lt;/p&gt;</description>
    </item>
    <item>
      <title>LLM Glossary for People Who Build Stuff</title>
      <link>https://ryanmpoe.com/2023-10-07-llm-nonsense-for-engineers/</link>
      <pubDate>Sat, 07 Oct 2023 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2023-10-07-llm-nonsense-for-engineers/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve done a number of AI-related projects. Nothing huge, and nothing from the metal, like training models. (I don&amp;rsquo;t have a year&amp;rsquo;s salary to set on fire!) If you don&amp;rsquo;t do a ton of Python (I don&amp;rsquo;t), some of the techniques appear at first glance magical. They&amp;rsquo;re referenced in &lt;a href=&#34;https://arxiv.org&#34;&gt;arxiv&lt;/a&gt; papers with fancy names like &amp;ldquo;embeddings,&amp;rdquo; or &amp;ldquo;quantization,&amp;rdquo; as if to whisper &amp;ldquo;this is above your pay grade 😉.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Nonsense.&lt;/p&gt;
&lt;p&gt;Most of the jargon in the LLM space exists because it hasn&amp;rsquo;t yet made its way out of academia. I have an extreme bias against gate-keeping language (which, as useful as jargon can be, often functions to keep the plebs out), but that&amp;rsquo;s another story. For those of us looking to use LLMs to build neat things, the learning curve can look like a cliff.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Testing your GitHub CODEOWNERS file</title>
      <link>https://ryanmpoe.com/2023-07-06-testing-github-codeowners/</link>
      <pubDate>Thu, 06 Jul 2023 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2023-07-06-testing-github-codeowners/</guid>
      <description>&lt;p&gt;I just realized after a few months that our GitHub code owners file wasn&amp;rsquo;t properly covering our migration files. I trust our code owners, and the misconfiguration is purely my fault. And, luckily, we didn&amp;rsquo;t lose any data to my mistake. No harm, no foul.&lt;/p&gt;
&lt;p&gt;But now it&amp;rsquo;s time to get serious with this. I made the change expecting it to work one way, but it worked another way. I didn&amp;rsquo;t test it. To fix it, the first step is asking &amp;ldquo;How do I test it?&amp;rdquo; Turns out, there&amp;rsquo;s a package for that!&lt;/p&gt;</description>
    </item>
    <item>
      <title>The Structural Problems with Abstractions</title>
      <link>https://ryanmpoe.com/2023-04-19-structural-problems-with-abstractions/</link>
      <pubDate>Wed, 19 Apr 2023 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2023-04-19-structural-problems-with-abstractions/</guid>
      <description>&lt;p&gt;Abstractions are a wonderful software development tool, allowing one to take common business rules and consolidate them into a centralized location for improved testability and reuse. We trade complexity in multiple locations for complexity in a central place, so that our top-level function doesn&amp;rsquo;t need to understand all the intricacies of the underlying, abstracted tooling.&lt;/p&gt;
&lt;p&gt;Yet, even the best abstractions come with a cost in terms of understanding. Your average (strawman) Java code bases are a wonderland of deeply-nested, extremely abstract concepts like the &lt;a href=&#34;https://factoryfactoryfactory.net/&#34;&gt;famed &lt;code&gt;FactoryFactoryFactory&lt;/code&gt;&lt;/a&gt;. The person who built the &lt;code&gt;FactoryFactoryFactory&lt;/code&gt;, knows full well how it works and why it&amp;rsquo;s necessary. Everyone else? Enjoy your six months of onboarding, retreading every inevitably under-documented design decision your forebears made.&lt;/p&gt;</description>
    </item>
    <item>
      <title>What are generics?</title>
      <link>https://ryanmpoe.com/2023-04-10-what-are-generics/</link>
      <pubDate>Mon, 10 Apr 2023 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2023-04-10-what-are-generics/</guid>
      <description>&lt;p&gt;Developers unfamiliar with robust type systems that support generics might see the following code as a huge &amp;ldquo;WTF&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;filterUndefined&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;TArrayElementType&lt;/span&gt;&amp;gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;theArray&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;readonly&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;TArrayElementType&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;)[]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exclude&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;TArrayElementType&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;undefined&lt;/span&gt;&amp;gt;[] {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;theArray&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;theArray&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Exclude&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;TArrayElementType&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;undefined&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;isUndefined&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;item&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isUndefined&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;subject&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;unknown&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subject&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;subject&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The semantics you see, e.g., &lt;code&gt;&amp;lt;TArrayElementType&amp;gt;&lt;/code&gt;, after &lt;code&gt;filterUndefined&lt;/code&gt; is what&amp;rsquo;s known as a &lt;strong&gt;generic&lt;/strong&gt;. Generics let consumers of your API pass types known to them, but not by us, so that your API can work with and later utilize those passed-in types. Think of them like function arguments for types.&lt;/p&gt;</description>
    </item>
    <item>
      <title>⚒️ Coalesce!</title>
      <link>https://ryanmpoe.com/2023-03-24-coalesce/</link>
      <pubDate>Fri, 24 Mar 2023 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2023-03-24-coalesce/</guid>
      <description>&lt;p&gt;At WorkTango, we mandate 100% code coverage. Over the years, we have found a few handy, type-safe helpers to help us save on writing test cases where the scenario would add no value.&lt;/p&gt;
&lt;h1 id=&#34;the-problem&#34;&gt;The Problem&lt;/h1&gt;
&lt;p&gt;Consider this function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;&#34;&gt;&lt;code&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Attribute&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;displayName?&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// &amp;lt;-- Optional
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * Given an at tribute, returns its display name if present. Otherwise, returns
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; * the attribute&amp;#39;s name.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getAttributeDisplayNameOrName&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;attribute&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Attribute&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;attribute&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;displayName&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;attribute&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;We use static analysis tools to determine coverage branches. This function, because it uses what is in effect a ternary, has two logical branches: the &lt;code&gt;displayName&lt;/code&gt; branch, and the &lt;code&gt;name&lt;/code&gt; branch. (It&amp;rsquo;s roughly equivalent to &lt;code&gt;attribute.displayName ? attribute.displayName : attribute.name&lt;/code&gt;.) To achieve 100% coverage, our test cases (assuming Jest) would need to be:&lt;/p&gt;</description>
    </item>
    <item>
      <title>Type Safe Object Access</title>
      <link>https://ryanmpoe.com/2021-04-04-type-safe-object-access/</link>
      <pubDate>Sun, 04 Apr 2021 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2021-04-04-type-safe-object-access/</guid>
      <description>&lt;h3 id=&#34;the-problem&#34;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;In our codebase over at &lt;a href=&#34;https://www.kazoohr.com/&#34;&gt;KazooHR.com&lt;/a&gt;, we generate all of our code with &lt;code&gt;@graphql-codegen&lt;/code&gt;. We use those types (mostly) as the canonical records for our business objects in the frontend (and for a lot of backend operations). Generating this code automatically gives us type safety for most of our backend and frontend code.&lt;/p&gt;
&lt;p&gt;We also enforce 100% code coverage. At first, it was onerous, but everyone quickly becomes &lt;em&gt;very good&lt;/em&gt; at Jest, so that the bus factor is actually rethinking code so that it&amp;rsquo;s more easily testable. This rethinking almost always results in cleaner, terser, and more functional code &lt;em&gt;and&lt;/em&gt; tests. It&amp;rsquo;s slower at first, but pays off HUGE in the long run.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Code Quality and Formatters</title>
      <link>https://ryanmpoe.com/2019-02-06-code-quality-and-formatters/</link>
      <pubDate>Wed, 06 Feb 2019 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2019-02-06-code-quality-and-formatters/</guid>
      <description>&lt;p&gt;We should respect process. Is exists to provide accountability and known pipelines for the movement of features from your development box to production sites.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Code quality and standards are often difficult to make a business case for. Why should we carve out time to improve years of unprofessional, difficult-to-read code when there will be no actual improvement to what we deliver to customers?&lt;/p&gt;
&lt;p&gt;In addition, lot of developers have very strong opinions about what counts as &amp;ldquo;readable&amp;rdquo; code. Have mercy on you if you have to decide between tabs or spaces when two developers have strongly-held opinions on one or the other; or whether four spaces or two is appropriate.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Sublime Text 3 from the command line</title>
      <link>https://ryanmpoe.com/2018-08-21-sublime-text-3-from-the-command-line/</link>
      <pubDate>Tue, 21 Aug 2018 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2018-08-21-sublime-text-3-from-the-command-line/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;http://www.sublimetext.com/&#34;&gt;Sublime Text 3&lt;/a&gt; is awesome. I won&amp;rsquo;t talk about that now.&lt;/p&gt;
&lt;p&gt;What I did want to quickly blog about is the command line utility. I&amp;rsquo;ve got a nice long list of bash aliases that I&amp;rsquo;ll blog about sometime in the future. One thing I wanted was a way to launch Sublime from the command line. In my search, I found &lt;a href=&#34;https://olivierlacan.com/posts/launch-sublime-text-3-from-the-command-line/&#34;&gt;Olivier Lacan&amp;rsquo;s nice writeup about setting up the Sublime CLI&lt;/a&gt;. It&amp;rsquo;s simple.&lt;/p&gt;</description>
    </item>
    <item>
      <title>RequireJS and Magento 2</title>
      <link>https://ryanmpoe.com/2017-04-12-requirejs-and-magento-2/</link>
      <pubDate>Wed, 12 Apr 2017 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2017-04-12-requirejs-and-magento-2/</guid>
      <description>&lt;p&gt;Magento2 uses &lt;a href=&#34;http://requirejs.org/&#34;&gt;RequireJS&lt;/a&gt; to load its javascript. The library is made to be a browser-based solution to a classic problem in frontend development: how to incorporate third-party dependencies in your javascript. Rather than having to place a page’s dependencies in script tags in each page’s header, RequireJS handles everything for you asynchronously, allowing your javascript to define its own dependencies, and allowing a &lt;a href=&#34;https://en.wikipedia.org/wiki/Progressive_enhancement&#34;&gt;progressive-enhancement-oriented&lt;/a&gt; workflow. More of a visual learner? Check out this article in &lt;a href=&#34;https://www.youtube.com/watch?v=TMAVXa-KB2Q&#34;&gt;YouTube video form: RequireJS and Magento2&lt;/a&gt;!&lt;/p&gt;</description>
    </item>
    <item>
      <title>A Magento 2 Frontend Workflow</title>
      <link>https://ryanmpoe.com/2017-04-07-magento-2-frontend-workflow/</link>
      <pubDate>Fri, 07 Apr 2017 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2017-04-07-magento-2-frontend-workflow/</guid>
      <description>&lt;p&gt;This short tutorial assumes you have Magento2 installed and ready to go. I’m using a local install on OSX, but these same commands and principles will work if you install Mage2 via Docker or Vagrant, as well. &lt;strong&gt;Note:&lt;/strong&gt; for a visual dive into this subject, check out the &lt;a href=&#34;http://web.archive.org/web/20171014182901/https://www.youtube.com/watch?v=RxMJGocq-lY&amp;amp;t=1s&#34;&gt;YouTube video I made on the subject, A Magento2 Frontend Workflow&lt;/a&gt;. Magento2 ships with LESS because its easy to use PHP to compile LESS into CSS. When they made that architectural decision, there was no PHP SCSS implementation. Mage2 aims to have as few requirements as possible, however, so they want to ensure you can run Magento on a standard LAMP box. But you can’t really do quick changes if you need Mage2 to compile your static assets. The &lt;code&gt;php bin/magento setup:static-content:deploy&lt;/code&gt; command takes minutes to compile. So to increase the development speed, Mage2 ships with a Grunt build process for those who want to take advantage of it.&lt;/p&gt;</description>
    </item>
    <item>
      <title>When to Use CSS Grids</title>
      <link>https://ryanmpoe.com/2017-03-20-when-to-use-css-grids/</link>
      <pubDate>Mon, 20 Mar 2017 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2017-03-20-when-to-use-css-grids/</guid>
      <description>&lt;p&gt;If you haven’t heard about CSS Grids yet, what are you doing? Go read about them, now! Support for them just dropped in the evergreen browsers, save Safari, which is due out in April. That means that by the end of the year, we could see as high as 50-70% support. (I just made this number up.)&lt;/p&gt;
&lt;p&gt;But as I’ve been working with a designer on what this means, and how we can best use CSS grids, I’ve come across a few limitations and things to be aware of before fully adopting CSS grids as the Swiss Army knife of layout tools…&lt;/p&gt;</description>
    </item>
    <item>
      <title>Magento 1.x Directory Structure Guide</title>
      <link>https://ryanmpoe.com/2016-08-19-magento-1-directory-structure-guide/</link>
      <pubDate>Fri, 19 Aug 2016 00:00:00 +0000</pubDate>
      <guid>https://ryanmpoe.com/2016-08-19-magento-1-directory-structure-guide/</guid>
      <description>&lt;p&gt;This past week, I’ve been taking a crash-course in Magento 1.x. I’ve been thoroughly impressed that it runs a fairly sophisticated ORM that implements an ActiveRecord-like API in an otherwise DataMapped pattern. It only calls a record’s resource handler (which saves the data to the database) when you call a row’s save() method. Pretty cool stuff. But holy moly, what a complex, spaghetti-like system. It took me forever (nearly 4 solid days) to finally figure out where I could find a module’s code, view files, and configuration. So I decided to share the guide I wrote up on it. &lt;strong&gt;TLDR;&lt;/strong&gt; Magento makes you put your modules in three primary places:&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
