<?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: Jacob Schatz</title>
    <description>The latest articles on DEV Community by Jacob Schatz (@jakecodes).</description>
    <link>https://dev.to/jakecodes</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%2F151003%2F00c0aca3-3134-4f4f-a73f-6dfe3a99ade3.jpg</url>
      <title>DEV Community: Jacob Schatz</title>
      <link>https://dev.to/jakecodes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jakecodes"/>
    <language>en</language>
    <item>
      <title>The Intriguing Issue of 429 Errors in Cloud Infrastructure</title>
      <dc:creator>Jacob Schatz</dc:creator>
      <pubDate>Wed, 26 Apr 2023 16:29:04 +0000</pubDate>
      <link>https://dev.to/caldev/the-curious-case-of-excessive-digitalocean-api-requests-my-startups-journey-through-cloudflare-and-ssl-certificates-33ph</link>
      <guid>https://dev.to/caldev/the-curious-case-of-excessive-digitalocean-api-requests-my-startups-journey-through-cloudflare-and-ssl-certificates-33ph</guid>
      <description>&lt;p&gt;In the realm of cloud infrastructure, unexpected challenges may arise, leading to complex problem-solving scenarios. One such instance occurred in Calendar.dev's Kubernetes deployment, where a peculiar bug caused 429 errors (too many requests) from DigitalOcean's API. This article details the investigative process and shares valuable insights for both learning and amusement.&lt;/p&gt;

&lt;p&gt;Upon deploying Calendar.dev via CI/CD, 429 errors were received from DigitalOcean's API, implying over 250 requests per minute. Despite confidence in making fewer requests during deployment, DigitalOcean representatives insisted otherwise. In search of a resolution, the number of replicaSets was reduced, but even with just one, an image pull error emerged from Kubernetes. A careful count revealed no more than 10 requests made to DigitalOcean within a 10-minute window, leaving the situation perplexing.&lt;/p&gt;

&lt;p&gt;Further investigation revealed that the rate limit error stemmed from domain checking. The primary issue was traced to a recent domain change to calendar.dev. An outdated Kubernetes (k8s) cert-manager had not been updated to refresh the HTTPS certificate after the domain switch. Consequently, cert-manager attempted to provision new certificates but failed to validate the domain, as it was incorrectly pointing to the external load balancer IP.&lt;/p&gt;

&lt;p&gt;DNS was initially dismissed as a potential problem, considering that the SSL certificate functioned without issue, and the error only surfaced when altering the number of replicaSets. The absence of an invalid SSL certificate warning was attributed to Cloudflare, which automatically provided an SSL certificate when recently added to the mix. Unaware of this automatic provisioning, cert-manager continued to run, leading to an unintended overlap of SSL certificates.&lt;/p&gt;

&lt;p&gt;In summary, each production deployment was met with 429 errors as Kubernetes attempted to spin up new pods. The excessive API requests to DigitalOcean were a result of Kubernetes trying to renew an improperly configured SSL certificate pointing to the incorrect domain. The lack of a visible error, caused by Cloudflare's automatic SSL certificate provision, masked the issue.&lt;/p&gt;

&lt;p&gt;In hindsight, the DNS problem was difficult to identify without any apparent DNS issues, and the focus was directed toward potential Kubernetes configuration mistakes. This was an interesting experience and underscores the importance of understanding interactions between cloud infrastructure components, such as Cloudflare and SSL certificates, to prevent similar challenges. &lt;/p&gt;

&lt;p&gt;There's an old saying:&lt;br&gt;
No, it is not a compiler error. It is never a compiler error.&lt;br&gt;
When you're at your wits end trying to solve a bug you can often assume the worst which can stop you from exploring other areas and just saying "what if".&lt;/p&gt;

</description>
      <category>429</category>
      <category>kubernetes</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Cron's Request Access Flow</title>
      <dc:creator>Jacob Schatz</dc:creator>
      <pubDate>Fri, 24 Jun 2022 11:37:27 +0000</pubDate>
      <link>https://dev.to/jakecodes/crons-request-access-flow-317o</link>
      <guid>https://dev.to/jakecodes/crons-request-access-flow-317o</guid>
      <description>&lt;p&gt;When you're making an app, things are often far more complicated on the inside than meets the eye.&lt;/p&gt;

&lt;p&gt;I love &lt;a href="https://twitter.com/Cron"&gt;Cron&lt;/a&gt;! Currently it is invite only, and I was lucky to get early access. Cron has an excellent onboarding flow and I wanted to document it, if only for myself as I build my own apps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lucid.app/publicSegments/view/8fa1c863-be88-4e32-8087-d825d774ab64/image.png"&gt;&lt;br&gt;
    &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iAtXrPDA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lucid.app/publicSegments/view/8fa1c863-be88-4e32-8087-d825d774ab64/image.png" width="880" height="622"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's &lt;a href="https://lucid.app/lucidchart/82d6aad7-0859-4e8a-9171-b901d3c1a53a/edit?invitationId=inv_e49cc770-78ab-4543-a229-8961a5ae5361#"&gt;a link&lt;/a&gt; to the Lucid Chart itself (sign in required).&lt;/p&gt;

</description>
    </item>
    <item>
      <title>From Director to Engineer</title>
      <dc:creator>Jacob Schatz</dc:creator>
      <pubDate>Mon, 29 Mar 2021 15:55:35 +0000</pubDate>
      <link>https://dev.to/jakecodes/from-director-to-engineer-1j8p</link>
      <guid>https://dev.to/jakecodes/from-director-to-engineer-1j8p</guid>
      <description>&lt;p&gt;I recently made the personal choice to change positions. At &lt;a href="https://dev.to/mural"&gt;MURAL&lt;/a&gt;, I joined as Director of Engineering for the Platform Pillar. I've been Director of Engineering at &lt;a href="https://mural.co"&gt;MURAL&lt;/a&gt; for just over 9 months and I love the job, and it's super rewarding. My team and I built a whole pillar and everyone is delivering. There is much work to do, but we got some serious shit done. I can't really express it any other way. We got shit done.&lt;/p&gt;

&lt;p&gt;To be honest, I'll have to be terse, but if you read this and you have questions feel free to reach out on &lt;a href="https://twitter.com/jakecodes"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The reason to join startups vs big co is because you can have a bigger impact. There are bigger companies out there, and I selfishly desire to be a very large part of the success of a something, even if it's small. I also desire to be one of the team, where the task may ambitious and the goals ambiguous, but we all are working our hardest to figure it out. As if others don't do that at Big Co's! I am sure they do.&lt;/p&gt;

&lt;p&gt;A Director position is a sought after position, so you might think I'm nuts for switching roles. Ultimately I see things that need to fixing, and I want to have an even more direct impact.&lt;/p&gt;

&lt;p&gt;I've been given the opportunity to work on the biggest problems at MURAL as an Engineer. This is really my dream job. To see problems, know the solution and have the freedom to fix them. At other companies, blockers may stand in your way of fixing big problems. It can kill a company if you can't execute quickly with high quality. I've been super lucky to have great mentors over the years. I've gained one often overlooked skill:&lt;/p&gt;

&lt;p&gt;It's one thing to know how to solve a problem, it's a completely different and (in my opinion) harder to execute big problems in a way that both brings people together and drives things forward. It's even harder to do all this with urgency. You can always bring people together by moving half the speed of smell, but who has time to move that slow?&lt;/p&gt;

&lt;p&gt;My new title is officially title is Principal Engineer, but really I see it as a way to use my skills as a solid dev to get shit done right away. &lt;/p&gt;

&lt;p&gt;Anyways. Back to work.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>management</category>
    </item>
    <item>
      <title>Performant Paintings: Building a Canvas Render Engine</title>
      <dc:creator>Jacob Schatz</dc:creator>
      <pubDate>Wed, 17 Mar 2021 18:53:29 +0000</pubDate>
      <link>https://dev.to/mural/performant-paintings-building-a-canvas-render-engine-4506</link>
      <guid>https://dev.to/mural/performant-paintings-building-a-canvas-render-engine-4506</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;We founded MURAL in 2011 with one goal in mind: create a feature-rich digital whiteboard, called a “mural,” for real-time online collaboration. As the company grew, our engineering team focused increasingly on streamlining performance. Then the COVID-19 pandemic hit, forcing companies around the world to rapidly adapt to working remotely. MURAL’s popularity surged, and with it came an influx of new and concurrent users. Increasingly large teams were suddenly coworking in MURAL to create ever more complex and varied content, which was slowing our app down. Performance was now our top priority, and we needed to address it as soon as possible.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;Initially, we developed MURAL to leverage the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction" rel="noopener noreferrer"&gt;DOM&lt;/a&gt;; as the base programming interface underlying the HTML, it’s familiar to web developers, making it a practical starting point for our application. However, the DOM was not designed to support dynamically changing interfaces comprising a massive number of graphical components — and MURAL users were creating new elements by the thousands. Each sticky note, image, and text box added to a mural further slowed the app’s performance, eventually leading to a bogged down and frustrating experience. &lt;/p&gt;

&lt;p&gt;A collaborative design thinking tool that freezes or lags is a collaborative design thinking tool that won’t be used; we needed to update our approach. To address the limitations of the DOM, we decided to go around it and migrate our application to &lt;a href="https://css-tricks.com/when-to-use-svg-vs-when-to-use-canvas" rel="noopener noreferrer"&gt;HTML5 canvas&lt;/a&gt;. There was just one problem: we’d designed our entire codebase around the DOM.&lt;/p&gt;

&lt;h1&gt;
  
  
  A Custom Solution for a Custom Problem
&lt;/h1&gt;

&lt;p&gt;Overnight, MURAL had become massively multiplayer, with concurrency and latency issues. In other words, large teams were working together in MURAL and slowing each other down. We needed to enable many users to collaborate simultaneously within a single mural without affecting the app’s performance, a challenge well traversed by the video game industry.  Who better to solve game developer problems than a game developer?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why performance in game development is important&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.dev/rail" rel="noopener noreferrer"&gt;Performance&lt;/a&gt; is often highly prioritized in game development; because games are a time-based medium, developers need to make sure their code doesn't slow down the frame rate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Enter Fede
&lt;/h1&gt;

&lt;p&gt;Fede, a former game developer, was familiar with building products using existing engines but had never built an engine for an existing product. Excited by the challenge, he began researching and planning his approach, starting with “low-level JavaScript.”&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is an engine?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The definition of an engine can get a bit nebulous. For both the purposes of this piece and game development, an engine can be thought of as a set of code libraries that handles low-level code. This code includes rendering, object management, audio, and user input.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Low-level programming isn’t generally associated with web development. When it is, it often means &lt;a href="https://webassembly.org" rel="noopener noreferrer"&gt;WebAssembly (WASM)&lt;/a&gt; is involved. However, Fede ruled out WASM at the time because of its more limited browser support in comparison to JavaScript. So what did he mean by low-level?&lt;/p&gt;

&lt;p&gt;Reminiscent of programming requirements in the financial tech sector, low-level JavaScript describes code in which performance informs every choice. For example, Fede pruned out unnecessary prototypal methods like &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;reduce&lt;/code&gt;, using &lt;code&gt;for&lt;/code&gt; loops instead, and structured the code to avoid using first-class functions while still keeping it readable and maintainable. While taking such an ascetic approach isn’t always necessary in general web development, it’s crucial in the case of a massively concurrently used app like MURAL.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is "C-like" JavaScript?&lt;/strong&gt;&lt;br&gt;
JavaScript automatically handles garbage collection. Therefore, web developers typically aren't concerned about memory allocation and the speed of method calls. However, when speed is the highest priority, techniques borrowed from low-level languages can powerfully boost performance. Some of these techniques include removing prototypal methods, avoiding passing functions as arguments, and ruthlessly managing memory (e.g., mutating an array in place rather than creating a new one).&lt;/p&gt;

&lt;p&gt;Here's an example. Compare the following two programs, which both identify the first character of every animal type that is exactly three letters long:&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;     &lt;span class="c1"&gt;// Common JavaScript approach&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animals&lt;/span&gt; &lt;span class="o"&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;fish&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;dog&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;cat&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;giraffe&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;firstCharOfThreeLetterNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;animals&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animal&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animal&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;


     &lt;span class="c1"&gt;// C-like JavaScript approach&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animals&lt;/span&gt; &lt;span class="o"&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;fish&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;dog&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;cat&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;giraffe&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;threeLetterAnimalTypes&lt;/span&gt; &lt;span class="o"&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;animals&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&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;length&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="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;threeLetterAnimalTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://jsbench.me" rel="noopener noreferrer"&gt;Benchmarking&lt;/a&gt; these programs against each other shows the second solution runs roughly 50% faster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  The Engines
&lt;/h1&gt;

&lt;p&gt;Our existing solution started as an experiment to transform user input into graphics and render them to the DOM. While this DOM-based approach served us well until now, it was eventually rendered obsolete by new needs. The existing code was also a monolith designed around painting to the DOM, and the complexity of the existing logic to translate MURAL concepts into renderable content made updating and testing the app a fiddly affair. So, in order to migrate MURAL to canvas in a maintainable way, Fede decided to start anew with a custom system consisting of two engines: one to handle rendering graphical primitives like triangles and circles, and one to translate MURAL objects, like sticky notes and gifs, into those primitives.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Rendering Engine
&lt;/h1&gt;

&lt;p&gt;The rendering engine has one responsibility: send visuals to a target. Unlike the other engine, this one has no handlers for MURAL-specific concepts.&lt;/p&gt;

&lt;p&gt;The rendering engine expects the graphical primitives output by the MURAL engine and renders them to the specified “surface” — anywhere murals are visible to users.&lt;/p&gt;

&lt;p&gt;Potential surfaces include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Canvas:&lt;/strong&gt; used in the MURAL application&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PDF or SVG&lt;/strong&gt;: used when exporting to these file formats&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;WebGL&lt;/strong&gt;: not yet implemented, but presents interesting potential three-dimensional applications&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The MURAL Engine
&lt;/h1&gt;

&lt;p&gt;The MURAL engine translates user-facing concepts into graphical primitives, by reducing them down to their most basic characteristics, represented by ASTs.&lt;/p&gt;

&lt;p&gt;For example, the aforementioned sticky note could be broken down into a colored rectangle, a border, and a text box.&lt;br&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%2Fuploads%2Farticles%2Fomxvfqkidssix0h4rwy1.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%2Fuploads%2Farticles%2Fomxvfqkidssix0h4rwy1.png" alt="Screenshot of a blue square with a black border that reads "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is an AST?&lt;/strong&gt;&lt;br&gt;
An Abstract Syntax Tree, or AST, is a structure that describes how data relates semantically to itself, or in other words, where each datum fits in a hierarchy with the rest. For example, an AST of a sentence might be broken down into verbs, adjectives, and nouns. &lt;a href="https://www.twilio.com/blog/abstract-syntax-trees" rel="noopener noreferrer"&gt;Read more&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Originally, the dual responsibilities of translating MURAL logic into graphical primitives and sending those primitives to render targets were handled by a single engine. So why did we now divide them into two?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns" rel="noopener noreferrer"&gt;Separating the concerns&lt;/a&gt; into two distinct engines enabled us to maintain, update, and build additional support infrastructure around each engine with less overhead going forward. This new design enabled Fede to build a test automation system, a key part of the new approach, alongside the rendering engine to ensure it worked as expected and that future changes didn’t slow down performance.&lt;/p&gt;

&lt;h1&gt;
  
  
  Challenges
&lt;/h1&gt;

&lt;p&gt;Of course, being the best solution for the job doesn’t mean canvas was simple to integrate. In order to encourage iteration and experimentation, A/B test, and because such a significant change could result in huge merge conflicts, new developments were incrementally merged to production, meaning both the old and new approaches had to work side by side until work was completed. &lt;/p&gt;

&lt;p&gt;Additional complications with our use case included canvas' complete lack of built-in accessibility support, which we as a company have &lt;a href="https://www.mural.co/accessibility-statement" rel="noopener noreferrer"&gt;committed to addressing&lt;/a&gt;; and its suboptimal support for &lt;a href="https://stackoverflow.com/questions/40066166/canvas-text-rendering-blurry/40074278#40074278" rel="noopener noreferrer"&gt;rendering text&lt;/a&gt; at the &lt;a href="https://en.wikipedia.org/wiki/Subpixel_rendering" rel="noopener noreferrer"&gt;subpixel level&lt;/a&gt; &lt;a href="https://medium.com/wdstack/fixing-html5-2d-canvas-blur-8ebe27db07da" rel="noopener noreferrer"&gt;across devices and resolutions.&lt;/a&gt; Text handling also happens to be under-described in the &lt;a href="https://www.w3.org/TR/css-text-3/#intro" rel="noopener noreferrer"&gt;CSS specification&lt;/a&gt;, meaning, for example, that each browser is free to decide when and how line breaks occur. While we could have built workarounds, they would have tacked on significant overhead for testing and maintenance, especially in today’s evergreen browser landscape, and our users create murals on a cornucopia of browsers.&lt;/p&gt;

&lt;p&gt;Fede chose to track his development progress with Chrome, because it represents 80% of our user base and leverages the GPU to accelerate &lt;a href="https://developers.google.com/web/updates/2012/07/Taking-advantage-of-GPU-acceleration-in-the-2D-canvas" rel="noopener noreferrer"&gt;canvas renders&lt;/a&gt;. To ensure all users had a smooth user experience, he also regularly tested and built across other browsers and devices. These manual tests included devices running Linux, Windows, macOS, Android, and iOS at a variety of hardware specifications, as our latency problem was a hardware issue, not a network one.&lt;/p&gt;

&lt;p&gt;By doing so, he discovered and addressed additional browser-specific restrictions, such as Safari’s variable &lt;a href="https://github.com/WebKit/WebKit/blob/c358cc276032fd1a25ff7269f47dad36a373a570/Source/WebCore/html/HTMLCanvasElement.cpp#L214" rel="noopener noreferrer"&gt;hardware limitation for renderable assets&lt;/a&gt; — Fede addressed this by &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas#performance_tips" rel="noopener noreferrer"&gt;offloading some of the rendering responsibilities to an off-screen canvas&lt;/a&gt; whenever the full mural is visible, which slows down scrolling at that zoom level in exchange for enabling the application to continue running smoothly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Outcomes
&lt;/h1&gt;

&lt;p&gt;After migrating from a monolithic DOM-focused architecture to our custom canvas rendering engine, we improved performance (as measured by the percentage of frames at 30fps or higher) by 80%. When Fede ran into a character painting issue in Chrome during the course of development, he contributed to its resolution by submitting a detailed report with solid reproduction cases. &lt;/p&gt;

&lt;p&gt;Because we separated the rendering process from the application-specific logic, they were no longer intertwined. The codebase became more maintainable and testable with automated end-to-end and visual tests, and by creating a system that can render to multiple targets, we opened up new opportunities for both internal and external developers to leverage MURAL features in a variety of exciting ways.&lt;/p&gt;

</description>
      <category>mural</category>
      <category>webdev</category>
      <category>gamedev</category>
      <category>canvas</category>
    </item>
    <item>
      <title>Oh, The Hats You'll Wear</title>
      <dc:creator>Jacob Schatz</dc:creator>
      <pubDate>Thu, 10 Oct 2019 16:59:29 +0000</pubDate>
      <link>https://dev.to/jakecodes/oh-the-hats-you-ll-wear-5g02</link>
      <guid>https://dev.to/jakecodes/oh-the-hats-you-ll-wear-5g02</guid>
      <description>&lt;p&gt;&lt;a href="https://twitter.com/agm_dev/status/1180114086257991681"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VqJB8m1x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/79uxk3ltifc5n76c070b.png" alt="Tweet about Adrian Gonzalo's Tweet expressing his interest in doing the &amp;quot;idea to production&amp;quot; trip, but not knowing how to handle the &amp;quot;non-technical&amp;quot; stuff."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our first post on Subs prompted an interesting question (which, if you missed it, you can &lt;a href="https://subs.launchrock.com/"&gt;subscribe for updates here&lt;/a&gt;.) How does one person juggle the many parts of a startup? Even those outside of their domain? As a founder with many responsibilities, I try to focus on a few goals to achieve great results. My advice has no basis more reliable than my own meandering experience. &lt;a href="https://genius.com/Baz-luhrmann-everybodys-free-to-wear-sunscreen-lyrics"&gt;I will dispense this advice now&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First: I try to work on problems in as small a scope as possible. My time is precious and working on full-blown solutions (instead of small scoped ideas) would only take up more of it. Just like a tool that does too many things, I try to focus my startup on being the master of just one thing for now.&lt;/p&gt;

&lt;p&gt;Second: boring solutions always get the job done faster. That one major feature needs to work before anything else. That feature doesn't need to use the latest and greatest tech.&lt;/p&gt;

&lt;p&gt;Third: dogfood your product as soon as possible. Subs is currently serving as my dedicated password manager, so I know exactly what I need to fix next.&lt;/p&gt;

&lt;p&gt;Keeping those goals in mind, I use tools I'm already productive with and know well. New tools can be great, but it takes time to learn them. Introducing new tools to my workflow takes time. This gets a little more nuanced when it comes to the various tasks, but if it works, it works. Blame the plumber, not the plunger. Anything you practice, you'll get good at. Right now, I am practicing starting a startup, not learning new tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer
&lt;/h2&gt;

&lt;p&gt;As a developer, I've been using &lt;a href="https://www.sublimetext.com/"&gt;Sublime Text 3&lt;/a&gt; for just about ten years for everything because it's boring and it works. I don't need to worry about anything else, because Sublime just works for me. I want to focus on my code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt; handles all of my JS linting issues for much the same reason: it's boring and it works and it comes with &lt;a href="https://cli.vuejs.org/guide/"&gt;Vue CLI 3&lt;/a&gt;. It fixes my code for me! When it comes to JavaScript style preferences, I don't hold myself to many, except semicolons and two spaces, no tabs. Otherwise, I don't care, I've got things to do.&lt;/p&gt;

&lt;p&gt;For front-end development, I use &lt;a href="https://vuejs.org/"&gt;VueJS&lt;/a&gt; and &lt;a href="https://vuex.vuejs.org/"&gt;Vuex&lt;/a&gt;. I build with Vue CLI. Everything is super fast, performant, boring, and they work. I know how to solve almost any problem I come across. My backend is usually built with &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt; with &lt;a href="https://sequelize.org/"&gt;Sequelize&lt;/a&gt;. Both are very fast and robust libraries. They are also easy for others to pick up and learn. Anything related to authentication and authorization is done with &lt;a href="http://www.passportjs.org/"&gt;PassportJS&lt;/a&gt;, a very solid and well-tested piece of middleware, that's also driving all OAuth2 authentication with Google in this project. Their documentation is short and to the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  UX Research and UI Design
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://balsamiq.com/wireframes/desktop/"&gt;Balsamiq Mockups&lt;/a&gt; was my tool of choice for anything design-related, but now I just use VueJS with &lt;a href="https://bulma.io/"&gt;Bulma&lt;/a&gt; for prototyping because it's quicker for me to write the code in the first place. Bulma also makes it easy to apply your own styles and doesn't require any JavaScript, which is fantastic. I love the idea of shipping a framework with only SASS/SCSS/CSS.&lt;/p&gt;

&lt;p&gt;My user experience research is pretty straightforward: I call a friend and ask them to try it out. Later I will add more steps, but I want to get as much user feedback as possible, but only when it's ready. I want to impress people with an awesome product.&lt;/p&gt;

&lt;p&gt;If they can, they'll give it a go and provide me with some feedback. I'll fix things up, then call more people, have them try it out, and get their feedback as well. Every single problem these first sets of users have become top-priority for me to fix. Then I rinse and repeat, collecting as much feedback as I can.&lt;/p&gt;

&lt;p&gt;While all of this is going on, I try and remember that data points are not trends. Before throwing time and energy at a fix, confirm that one user's problems are others' problems as well. At this stage, you want to fix things that have the largest impact on the overall experience for your user base.&lt;/p&gt;

&lt;h2&gt;
  
  
  Marketing
&lt;/h2&gt;

&lt;p&gt;For now, I'll be doing &lt;a href="https://twitter.com/subs_hq"&gt;a lot of marketing on Twitter&lt;/a&gt;. I'm admittedly not the best at tweeting. If I can find someone to help with writing tweets, I do, but I also make sure that I'm documenting the process of building Subs rather than spitting out memes. Every tweet has to be meaty, which means I have to develop things worth tweeting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/subs/"&gt;Articles are also a big part of my marketing toolset&lt;/a&gt;, so I have someone helping me. We have a call or a video chat to talk about article ideas. They write down some notes and start working on an outline and drafts. We'll meet regularly to go over the drafts together to check for tone and make some changes, and I'll approve of a final draft that gets posted online. This saves me a ton of time and keeps my tone in articles.&lt;/p&gt;

&lt;p&gt;Ultimately, when it comes to marketing, I'll do anything that saves time. My goals are to build a very solid version of the product, get it into people's hands, and get feedback. The less I need to worry about drafting and copy editing, the more time I can spend writing excellent code. Which, I think, is something to write about. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sales and Support
&lt;/h2&gt;

&lt;p&gt;Selling your idea sometimes means ignoring anything that might distract you from getting it out of the door. Even when you're getting started, you might hear suggestions and requests from big companies, if you are lucky. The best thing you can do is to save these requests and ignore them for now. Give the big company &lt;a href="https://docs.gitlab.com/ee/user/project/issues/"&gt;a platform to give you feedback&lt;/a&gt; so that you have everything saved for later.&lt;/p&gt;

&lt;p&gt;Create an issue tracker (like &lt;a href="https://gitlab.com/gitlab-org/gitlab-foss/issues"&gt;GitLab issues&lt;/a&gt;) where everyone can submit feature requests. That way, you can interact with your user base and they'll know their voice is heard, which is what you want! Don't let it distract from your goal of releasing version 1.0, and don't be intimidated by big companies. It's valuable feedback, but as a founder and wearer of many hats, you have limited bandwidth. Your focus should be on the smallest scope possible, on what gets your product built and working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opportunity Creation
&lt;/h2&gt;

&lt;p&gt;Building a product on my own is hard work, so something positive I work towards is creating opportunities for others. I enjoy offering people the chance to learn the way I learned. I try to give newer developers the chance to help me out if it helps them out. Letting a junior dev watch you build your product and ask questions, assuming the right &lt;a href="https://en.wikipedia.org/wiki/Non-disclosure_agreement"&gt;non-disclosure agreements&lt;/a&gt; are in place, is a great way to keep me accountable and for them to learn.&lt;/p&gt;

&lt;p&gt;I'm sure by now you can sense a theme running through the way I work. The latest and greatest in languages, platforms, and frameworks are always exciting, but not when it comes to getting things done. I need to be able to work quickly to gets Subs to you, and that means boring and reliable methods. If you want to see the proof and stay updated, sign up for updates at &lt;a href="https://subshq.launchrock.com"&gt;https://subshq.launchrock.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>javascript</category>
      <category>security</category>
    </item>
    <item>
      <title>Big Giant Vue Apps</title>
      <dc:creator>Jacob Schatz</dc:creator>
      <pubDate>Thu, 03 Oct 2019 18:52:50 +0000</pubDate>
      <link>https://dev.to/jakecodes/big-giant-vue-apps-5048</link>
      <guid>https://dev.to/jakecodes/big-giant-vue-apps-5048</guid>
      <description>&lt;p&gt;I recently took a huge risk and left Netlify to pursue an opportunity of a lifetime to start a startup. Subs is currently in development. You &lt;a href="http://subshq.launchrock.com/" rel="noopener noreferrer"&gt;can read all about it on our Launchrock&lt;/a&gt;, and sign up for updates.&lt;/p&gt;

&lt;p&gt;Subs makes password management seamless by removing the need to constantly enter a master password. I am joining this startup with someone I deeply respect, and I'm already heads-down working hard on this exciting new project. This isn't my first time writing a new product from idea to production, but it's already introducing me to new and interesting challenges.&lt;/p&gt;

&lt;p&gt;For this project, the architecture is going to be a bit different from what I've done in the past. Typically, I've used what I know well: a web app that evolves over time into something bigger. Instead of offering a ton of decent features to a few platforms, the plan is to offer a single, well-built, performant major feature to a multitude of platforms — web, mobile, desktop, and browser extensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture and Tooling
&lt;/h2&gt;

&lt;p&gt;The codebase for this startup will be large, using one mono-repo to house eight individual projects covering all of our targeted platforms. We'll also have an additional "common" project to house all of the shared components, services, and stores.&lt;/p&gt;

&lt;p&gt;This might seem like a lot in one repo, but there are many benefits to working this way.&lt;/p&gt;

&lt;p&gt;I'm putting the building blocks for cross-platform infrastructure in place now, so that future development is as easy as it can be. One thing I've learned: the prototype usually becomes the final code, so do it right the first time. Putting in the building blocks from the beginning allows me to give the major features the attention they need.&lt;/p&gt;

&lt;p&gt;The majority of the front end will be driven by Vue and NativeScript. &lt;a href="https://about.gitlab.com/2016/10/20/why-we-chose-vue/" rel="noopener noreferrer"&gt;I've spoken about the benefits of Vue in the past&lt;/a&gt;. For this project, using the Vue ecosystem to drive the web application, desktop application, and browser extensions affords a level of reusability across the mono-repo and "common" repo, so code won't need to be written twice.&lt;/p&gt;

&lt;p&gt;Nativescript with NativeScript-Vue will be used for the iOS and Android applications. While they won't benefit directly from the same shared components, it may let us share a Vuex store across all application instances to handle state, as well as sharing the services that call the API.&lt;/p&gt;

&lt;p&gt;Battle-tested libraries are necessary for encryption-related tasks. So, for extra security, I wrote a command-line interface in Python using &lt;a href="https://github.com/sindresorhus/execa" rel="noopener noreferrer"&gt;execa&lt;/a&gt; for process execution. This buys us flexibility in the future. If we want to rewrite the CLI in another language like Rust or Go for greater performance, it would be a drop-in replacement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&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%2Fi.imgur.com%2Fz7VpLtz.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%2Fi.imgur.com%2Fz7VpLtz.png" alt="Diagram of Subs' project structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram might be confusing so let me explain. The project features at least four Vue CLI generated applications: a web application, a desktop application, a browser extension, and the common library.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;common&lt;/code&gt; library does not have a build step. It is simply imported into projects. When a project using the &lt;code&gt;common&lt;/code&gt; modules is built, the &lt;code&gt;common&lt;/code&gt; library gets baked in. I do have to run &lt;code&gt;yarn link&lt;/code&gt; initially and link it manually, but it's in my &lt;code&gt;README&lt;/code&gt; for the future. This way, any changes I make to the &lt;code&gt;common&lt;/code&gt; library immediately benefit from &lt;a href="https://webpack.js.org/concepts/hot-module-replacement/" rel="noopener noreferrer"&gt;hot module replacement&lt;/a&gt;, and I can see the changes immediately. &lt;/p&gt;

&lt;p&gt;I can use my shared components in the web app like so:&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%2Fi.imgur.com%2F833T3dr.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%2Fi.imgur.com%2F833T3dr.png" alt="code screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's cool about this (besides the fact that I'm using &lt;a href="https://www.sublimetext.com/" rel="noopener noreferrer"&gt;Sublime Text&lt;/a&gt;) is that my components library is empty minus an entry point.&lt;/p&gt;

&lt;p&gt;I am importing every single component at once which &lt;strong&gt;does not&lt;/strong&gt; take advantage of &lt;a href="https://webpack.js.org/guides/tree-shaking/" rel="noopener noreferrer"&gt;tree shaking&lt;/a&gt; as much as it would in other apps. If that was a concern, I'd restructure my exports in my &lt;code&gt;common&lt;/code&gt; library. In my case, my app is very simple, so I can get away with this.&lt;/p&gt;

&lt;p&gt;Each app within &lt;code&gt;packages&lt;/code&gt; has its own managed &lt;code&gt;node_modules&lt;/code&gt;. And I have to run &lt;code&gt;yarn&lt;/code&gt; on each of the eight projects. And I have to run &lt;code&gt;yarn link subscommon&lt;/code&gt; everywhere I want to use my common library.&lt;/p&gt;

&lt;p&gt;Is this a boring solution? It is a marginally longer setup that isn't a big deal to me. I like my setups to be explicit but not tedious. I like less magic and more understanding. The total time to set up the project is about ten minutes. Could it have been three minutes? Maybe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing State and Services
&lt;/h2&gt;

&lt;p&gt;I also share the Vuex store and my services. Since Vuex is the only part of my app that uses the services, they simply get imported on their own.&lt;/p&gt;

&lt;p&gt;When Vuex initializes, it performs &lt;a href="https://github.com/vuejs/vuex/blob/dev/src/mixin.js#L22" rel="noopener noreferrer"&gt;a recursive injection&lt;/a&gt; of the store into all the child components, so you need to make sure it is using the right Vue instance. Here's my shared Vuex store:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Vuex from "vuex";
import menu from "./menu";
import user from "./user";

export default function store(Vue) {
  Vue.use(Vuex);
  const store = new Vuex.Store({
    modules: {
      menu,
      user,
      ... more stuff
    }
  });
  return store;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I don't import Vue above because I pass Vue into my web app and desktop app like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Vue from "vue";
import App from "./App.vue";
import { store } from "subscommon";

Vue.config.productionTip = false;

new Vue({
  store: store(Vue),
  render: h =&amp;gt; h(App)
}).$mount("#app");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now Vuex can latch onto the app properly and populate any child components with &lt;code&gt;$store&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sum of All Parts
&lt;/h2&gt;

&lt;p&gt;This project structure serves its purpose by allowing me to iterate on the primary application quickly, across various target platforms. &lt;/p&gt;

&lt;p&gt;How quick? I was able to initialize &lt;a href="https://github.com/nklayman/vue-cli-plugin-electron-builder" rel="noopener noreferrer"&gt;Vue CLI Electron Builder&lt;/a&gt;, import my store, and have an exact running copy of my desktop app in five minutes, with no code changes. &lt;/p&gt;

&lt;p&gt;I was able to get my Chrome extension running almost as quickly too, but admittedly it took me a few hours to understand how Chrome extensions work, so some time was spent reading the docs. Using the &lt;a href="https://github.com/adambullmer/vue-cli-plugin-browser-extension" rel="noopener noreferrer"&gt;Vue CLI Plugin Browser Extension&lt;/a&gt; got me up and running very quickly, promising minimal code changes between all extensions. I'll believe it when I see it, but I haven't gotten there yet.&lt;/p&gt;

&lt;p&gt;Meanwhile, my desktop app is so damn small and fully functional. Here's the file structure:&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%2Fi.imgur.com%2FUdsvbek.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%2Fi.imgur.com%2FUdsvbek.png" alt="Screenshot of App.vue file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I kept &lt;code&gt;HelloWorld&lt;/code&gt; as the entry point to all my apps because it's the way Vue CLI 3 scaffolds it and it makes sense to me. I'm sure that, at some point, some developers will tell me it's not kosher and to change it. Until then, I'm keeping it.&lt;/p&gt;

&lt;p&gt;We hope to get a working proof-of-concept into your hands very soon so we can hear what you think. If you are interested in what I am building and want to know when my beta launches, &lt;a href="http://subshq.launchrock.com/" rel="noopener noreferrer"&gt;then sign up on Launchrock&lt;/a&gt; and I'll keep you updated.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
