<?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>Forem: CodeNameGrant</title>
    <description>The latest articles on Forem by CodeNameGrant (@codenamegrant).</description>
    <link>https://forem.com/codenamegrant</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%2F194874%2F2da487c1-ca5b-4014-aa05-b0595498a6ae.jpg</url>
      <title>Forem: CodeNameGrant</title>
      <link>https://forem.com/codenamegrant</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/codenamegrant"/>
    <language>en</language>
    <item>
      <title>Domain-First Nx Monorepos: Using `packages/` to Make Ownership and Boundaries Obvious</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Mon, 23 Feb 2026 11:31:51 +0000</pubDate>
      <link>https://forem.com/codenamegrant/domain-first-nx-monorepos-using-packages-to-make-ownership-and-boundaries-obvious-4h5g</link>
      <guid>https://forem.com/codenamegrant/domain-first-nx-monorepos-using-packages-to-make-ownership-and-boundaries-obvious-4h5g</guid>
      <description>&lt;p&gt;&lt;em&gt;Where should this go? Which apps are using this library?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Those two questions are plaguing my team as our monorepo grows. Not because we don’t know Nx, TypeScript, or React—but because our folder structure doesn’t clearly communicate &lt;strong&gt;ownership&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We started with a pretty standard setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;apps/&amp;lt;app&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;libs/&amp;lt;domain&amp;gt;/&amp;lt;lib&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;libs/shared/&amp;lt;lib&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That structure was a great starting point (this was our first real dive into monorepo architecture). It was simple, it scaled &lt;em&gt;enough&lt;/em&gt;, and it let us move fast early on. But we’re now at ~40 libraries and growing, and it’s becoming less obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;where a new library should live, and&lt;/li&gt;
&lt;li&gt;whether a library is truly domain-specific or actually shared.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post explains why we plan to migrate to a &lt;strong&gt;domain-first &lt;code&gt;packages/&lt;/code&gt; layout&lt;/strong&gt;. The main goal being &lt;strong&gt;domain clarity&lt;/strong&gt; (and making module boundaries easier to enforce &lt;em&gt;consistently&lt;/em&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Flat &lt;code&gt;libs/&lt;/code&gt; worked… until it didn’t
&lt;/h2&gt;

&lt;p&gt;The hardest part of a growing monorepo isn’t the tooling—it’s the ambiguity. When everything lives side-by-side in &lt;code&gt;libs/&lt;/code&gt;, the folder structure stops answering basic questions, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who owns this library?&lt;/li&gt;
&lt;li&gt;Which app is it actually supporting?&lt;/li&gt;
&lt;li&gt;Is this genuinely shared, or did someone just reuse it once?&lt;/li&gt;
&lt;li&gt;If I’m adding a new thing, where’s the “obvious” place to put it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So instead of the repo guiding you, you end up relying on tribal knowledge (or you run &lt;code&gt;nx graph&lt;/code&gt; and play detective).&lt;/p&gt;

&lt;h3&gt;
  
  
  A concrete example (the kind that trips new people up)
&lt;/h3&gt;

&lt;p&gt;We have a &lt;code&gt;contract-web&lt;/code&gt; app and a library with a name that &lt;em&gt;sounds&lt;/em&gt; like it belongs to it—something like &lt;code&gt;data-access-contract&lt;/code&gt;. If you’re new to the workspace, you’d make a totally reasonable assumption:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Cool, contract-web uses data-access-contract.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Except… it doesn’t. &lt;code&gt;company-web&lt;/code&gt; is the one consuming it.&lt;/p&gt;

&lt;p&gt;Nothing about a flat folder structure makes that mismatch obvious. And this is where the time goes. Not in builds—mostly in the little moments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;onboarding takes longer than it should,&lt;/li&gt;
&lt;li&gt;“where should this live?” becomes a recurring message thread,&lt;/li&gt;
&lt;li&gt;libraries slowly drift into whatever location was convenient at the time,&lt;/li&gt;
&lt;li&gt;and cross-domain dependencies happen by accident.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point, our bottleneck isn’t build speed—it’s how quickly someone can answer: &lt;strong&gt;“Where does this code belong?”&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementing &lt;code&gt;packages/&lt;/code&gt;: make the repo explain itself
&lt;/h2&gt;

&lt;p&gt;What we want is simple: we want the repo structure to answer the questions we keep asking.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What domain does this code belong to?&lt;/li&gt;
&lt;li&gt;Is it shared, or is it local to one app?&lt;/li&gt;
&lt;li&gt;Where does a new feature go?&lt;/li&gt;
&lt;li&gt;If I’m trying to understand a domain, where do I start?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s what’s driving the move to a domain-first layout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;packages/&amp;lt;domain&amp;gt;/app/...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;packages/&amp;lt;domain&amp;gt;/libs/...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;packages/shared/libs/...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;packages/&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;“Packages” isn’t a new monorepo law; it’s just a common convention that communicates:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“These are grouped things that belong together under a single workspace.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nx supports many layouts. We’re choosing this one because it makes domain boundaries &lt;strong&gt;visible&lt;/strong&gt;, which reduces cognitive load.&lt;/p&gt;

&lt;h3&gt;
  
  
  Module boundaries: make the “right” thing the easy thing
&lt;/h3&gt;

&lt;p&gt;We’re also using this restructure to make module boundaries easier to reason about and enforce. Nx boundaries are tag-based, so the plan is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tag everything in a domain with &lt;code&gt;domain:&amp;lt;domain&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;tag shared libs with &lt;code&gt;domain:shared&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then enforce a few simple rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A domain app can import:

&lt;ul&gt;
&lt;li&gt;libraries from its own domain, and&lt;/li&gt;
&lt;li&gt;libraries from &lt;code&gt;domain:shared&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Domain libraries should not import from other domains&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;shared&lt;/code&gt; libraries should not import from domain libraries&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The real win here isn’t the lint rule itself—we can write those today. The win is that once the code is physically grouped by domain, those rules stop feeling like “extra process” and start feeling like the natural shape of the repo.&lt;/p&gt;

&lt;p&gt;In other words: developers don’t need deep context to do the right thing, and when someone does the wrong thing, Nx catches it early.&lt;/p&gt;




&lt;h2&gt;
  
  
  High-level migration plan (draft)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Use the &lt;code&gt;@nx/workspace:move&lt;/code&gt; generator to do the heavy lifting when moving projects in an Nx Workspace&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;[ ] &lt;strong&gt;Create the new &lt;code&gt;/packages&lt;/code&gt; skeleton (&lt;code&gt;/packages/&amp;lt;domain&amp;gt;&lt;/code&gt; and &lt;code&gt;/packages/shared&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;[ ] &lt;strong&gt;Move each app one at a time from &lt;code&gt;apps/&amp;lt;app&amp;gt;&lt;/code&gt; → &lt;code&gt;packages/&amp;lt;domain&amp;gt;/app&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Tag each project with &lt;code&gt;domain:&amp;lt;domain&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Verify it’s visible in &lt;code&gt;nx graph&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;[ ] &lt;strong&gt;Move the libs that are already shared from &lt;code&gt;libs/shared/*&lt;/code&gt; → &lt;code&gt;packages/shared/libs/*&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Tag each project with &lt;code&gt;domain:shared&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Rename projects only when it’s an easy win during the move (otherwise leave names alone for now)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;[ ] &lt;strong&gt;Move “definitely domain-local” libs (one domain at a time)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] For a given domain, move only the libs we’re confident are &lt;strong&gt;not&lt;/strong&gt; shared&lt;/li&gt;
&lt;li&gt;[ ] Tag each project with &lt;code&gt;domain:&amp;lt;domain&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;[ ] &lt;strong&gt;Handle the “maybe shared” libs (after the obvious stuff is moved)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] For each “maybe shared” lib: keep it &lt;strong&gt;domain-local&lt;/strong&gt; or intentionally promote it to &lt;strong&gt;shared&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Tag each project appropriately&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;[ ] &lt;strong&gt;Clean-up pass: rename projects that still need it&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Standardize remaining project names to match the new conventions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;If this works the way we expect, the best sign will be that people stop asking where things should go—because the answer will be obvious.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus benefits
&lt;/h2&gt;

&lt;p&gt;None of this is guaranteed for every Nx monorepo. These benefits are mostly a product of &lt;strong&gt;how our repo works today&lt;/strong&gt;. We also may not implement all of these immediately—think of them as “now possible / now easier” rather than promised outcomes.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Simpler GraphQL Code Generator workflow
&lt;/h3&gt;

&lt;p&gt;In our environment, GraphQL &lt;code&gt;codegen&lt;/code&gt; is currently very flexible (flags for different services and libs), but that flexibility comes with cognitive overhead and occasional noisy regen. With &lt;code&gt;packages/&amp;lt;domain&amp;gt;/*&lt;/code&gt;, we can shift the default workflow to: “I’m working on this app → run codegen for this app/domain”, and have it target:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;packages/&amp;lt;domain&amp;gt;/libs/*&lt;/code&gt;, plus&lt;/li&gt;
&lt;li&gt;&lt;code&gt;packages/shared/libs/*&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That trades some granularity for a workflow that’s harder to mess up.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Apollo type policies become easier to wire consistently
&lt;/h3&gt;

&lt;p&gt;Right now, type policies live in feature libs and apps manually import them. That’s workable, but easy to forget as features move or get reused. Once the repo is domain-grouped, it becomes straightforward to generate an app-level “policy aggregator” (per domain app) that automatically pulls in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;domain policies exported from all domain-local libs, plus&lt;/li&gt;
&lt;li&gt;shared policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This won’t magically solve all policy design problems, but it reduces the “remember to wire it up in every app” overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) Generators become much more predictable (domain + type = obvious destination)
&lt;/h3&gt;

&lt;p&gt;With a flat &lt;code&gt;libs/&lt;/code&gt; directory, a generator can create a library, but it can’t reliably answer: &lt;em&gt;which app/domain should this belong to?&lt;/em&gt; That decision often depends on local context.&lt;/p&gt;

&lt;p&gt;After the migration, we can write (or customize) generators that take inputs like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;domain (&lt;code&gt;company&lt;/code&gt;, &lt;code&gt;contract&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;type (&lt;code&gt;feature&lt;/code&gt;, &lt;code&gt;ui&lt;/code&gt;, &lt;code&gt;data-access&lt;/code&gt;, …)&lt;/li&gt;
&lt;li&gt;name (&lt;code&gt;invoices&lt;/code&gt;, &lt;code&gt;teams&lt;/code&gt;, …)
…and always place the output in a predictable location, e.g.:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;packages/company/libs/feature-invoices&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of those quality-of-life improvements that compounds over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) &lt;em&gt;Possible&lt;/em&gt; future win: more predictable implicit libraries
&lt;/h3&gt;

&lt;p&gt;This one is more of an idea than a guaranteed outcome, but a domain-first folder structure should make Nx’s “implicit libraries” approach a lot more predictable, because the filesystem starts encoding intent (domain + shared) instead of everything living in one flat space.&lt;/p&gt;

&lt;p&gt;If we go down this route later, the hope is we can model impacts with fewer manual touch points—similar to the approach described here: &lt;a href="https://medium.com/marmicode/nx-implicit-libraries-the-hidden-gem-d965d5118ecd" rel="noopener noreferrer"&gt;Nx Implicit Libraries: The Hidden Gem&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Ultimately, we want a monorepo that’s easy to navigate, hard to misuse, and doesn’t require tribal knowledge to be productive.&lt;/p&gt;

</description>
      <category>nx</category>
      <category>typescript</category>
      <category>architecture</category>
      <category>monorepo</category>
    </item>
    <item>
      <title>Flip Your CI Like a Switch: Instantly Toggle Flags with GitHub Actions Variables</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Thu, 16 Oct 2025 07:48:27 +0000</pubDate>
      <link>https://forem.com/codenamegrant/flip-your-ci-like-a-switch-instantly-toggle-flags-with-github-actions-variables-2go9</link>
      <guid>https://forem.com/codenamegrant/flip-your-ci-like-a-switch-instantly-toggle-flags-with-github-actions-variables-2go9</guid>
      <description>&lt;p&gt;Ever wish you could tweak your CI pipeline on the fly—change logging levels, skip caching, or toggle features—&lt;strong&gt;without touching a single line of code or YAML&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;GitHub Actions variables&lt;/strong&gt;, you can.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are GitHub Actions Variables?
&lt;/h2&gt;

&lt;p&gt;Like GitHub Secrets (&lt;code&gt;secrets&lt;/code&gt;), &lt;strong&gt;Variables&lt;/strong&gt; (&lt;code&gt;vars&lt;/code&gt;) are simple key-value pairs you define in your repository.&lt;br&gt;&lt;br&gt;
You can use them to &lt;strong&gt;control workflow behavior without committing changes&lt;/strong&gt;, making them perfect for feature flags, build tweaks, or environment switches.&lt;/p&gt;


&lt;h2&gt;
  
  
  Example: Controlling Nx Build Flags
&lt;/h2&gt;

&lt;p&gt;Imagine you want to control verbose logging and cache usage for an &lt;code&gt;nx build&lt;/code&gt; command.&lt;br&gt;&lt;br&gt;
In your repo settings under &lt;strong&gt;Secrets and Variables → Actions → Variables (tab)&lt;/strong&gt; define the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NX_VERBOSE_MODE&lt;/code&gt;: &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NX_USE_NX_CACHE&lt;/code&gt;: &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then use them directly in your workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
  &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;npx nx build app-name&lt;/span&gt;
    &lt;span class="s"&gt;${{ vars.NX_VERBOSE_MODE == 'true' &amp;amp;&amp;amp; '--verbose' || '' }}&lt;/span&gt;
    &lt;span class="s"&gt;${{ vars.NX_USE_NX_CACHE != 'true' &amp;amp;&amp;amp; '--skip-nx-cache' || '' }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Using &lt;code&gt;run: &amp;gt;&lt;/code&gt; (folded style) combines all lines into a single line, replacing line breaks with a space. Its great in this use-case making the command easy to read.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now you can toggle flags directly from the &lt;strong&gt;Variables&lt;/strong&gt; tab—no pull requests, no commits, just instant control.&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>ci</category>
      <category>automation</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Monorepos: A Year in Review</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Thu, 04 Sep 2025 20:41:44 +0000</pubDate>
      <link>https://forem.com/codenamegrant/monorepos-a-year-in-review-16b8</link>
      <guid>https://forem.com/codenamegrant/monorepos-a-year-in-review-16b8</guid>
      <description>&lt;h2&gt;
  
  
  One Repository to Rule Them All
&lt;/h2&gt;

&lt;p&gt;Its been a year since we &lt;a href="https://dev.to/codenamegrant/monorepos-from-discovery-to-deployment-a-developers-journey-d9o"&gt;started migrating&lt;/a&gt; our suite of web applications into a monorepo workspace and I thought it was time share some wins and warts and how we made it all happen. &lt;/p&gt;

&lt;p&gt;The goal? To simplify dependency management, reduce code duplication, and make expanding the suite to include more apps or feature libraries easier. &lt;/p&gt;

&lt;p&gt;Our team is not the smallest team, but it is the &lt;em&gt;next&lt;/em&gt; smallest. With only a &lt;strong&gt;two-person development team&lt;/strong&gt;, every decision mattered—from the scope of the migration project to the order that apps would be moved and how to structure shared libraries. However a small team comes with it the advantage of being able to make &lt;strong&gt;rapid decisions&lt;/strong&gt; without lengthy debates, &lt;strong&gt;maintain a high visibility of changes&lt;/strong&gt;, and experiment with implementation options without affecting a large group of stakeholders.&lt;/p&gt;

&lt;p&gt;So lets Jump in, I'm excited to share the results!&lt;/p&gt;




&lt;h2&gt;
  
  
  Our Approach
&lt;/h2&gt;

&lt;p&gt;A major requirement during the migration was that we couldn’t pause new feature development or bug fixes on existing apps. So we chose to follow an &lt;strong&gt;incremental approach&lt;/strong&gt;: migrating one full app at a time (and all its parts). We started with a smaller app, so we could also focus on setting up the workspace, pipelines, and tooling before scaling to the other applications.&lt;/p&gt;

&lt;p&gt;To keep things manageable, we included a caveat, &lt;em&gt;"the app being migrated wouldn’t have new feature development scheduled"&lt;/em&gt;. Minor patches and bug fixes were fine, but juggling ongoing updates while also migrating those updates risked doubling work and introducing confusion.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tech Stack: Nx &amp;amp; Nx Cloud
&lt;/h3&gt;

&lt;p&gt;Of &lt;a href="https://monorepo.tools/?utm_source=chatgpt.com#tools-review" rel="noopener noreferrer"&gt;all the monorepo tools&lt;/a&gt;, we decided &lt;a href="https://nx.dev/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;&lt;strong&gt;Nx&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://nx.dev/nx-cloud?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;&lt;strong&gt;Nx Cloud&lt;/strong&gt;&lt;/a&gt; were the best fit for us. Its CLI is well-documented, with plenty of examples and tutorials, which made it feel less daunting to learn. This was important—if we couldn’t figure out how to use it effectively, not only would our migration stall, but stakeholders might start doubting whether moving to a monorepo was even a good idea. Nx also comes with plugins for our existing tech stack and Nx Cloud let us experiment extensively before starting the migration with a &lt;a href="https://nx.dev/nx-cloud#plans" rel="noopener noreferrer"&gt;generous free tier&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stories from the Journey
&lt;/h3&gt;

&lt;p&gt;Along the way, a few memorable challenges and discoveries taught us more than any checklist ever could.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Dayjs vs. Kendo Formatting Headache&lt;/strong&gt;&lt;br&gt;
We ran into a sneaky bug when our &lt;code&gt;dayjs&lt;/code&gt; formatter clashed with KendoReact. Day.js uses &lt;strong&gt;ISO formatting&lt;/strong&gt; (eg. &lt;code&gt;YYYY&lt;/code&gt; for a 4-digit year), while KendoReact expects its own notation (ie. &lt;code&gt;yyyy&lt;/code&gt;). The fix was to use Day.js to handle all calculations and comparisons, and leave all the formatting to KendoReact, keeping everything consistent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rolling Our Own Keycloak Wrapper&lt;/strong&gt;&lt;br&gt;
The React Keycloak wrapper we originally used was outdated and barely maintained. With no other alternatives, we built our own, learning token flows, refresh cycles, and how React talks to Keycloak. The result: a reliable, battle-tested wrapper and a team that actually understands how authentication works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Refactor That Ate the Repo&lt;/strong&gt;&lt;br&gt;
Refactoring a set of shared UI components started ith teh best of intentions, and suddenly the refactor had touched &lt;strong&gt;150+ files&lt;/strong&gt;. Nope! The PR was far too big to review safely, so we had to reverted it. Instead, we tackled updates piecemeal with smaller, focused PRs—easier reviews, less risk, and more confidence. &lt;em&gt;Lesson learned: big-bang changes feel efficient, but controlled iteration wins.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Documentation We Didn’t Know We Needed&lt;/strong&gt;&lt;br&gt;
We’d never focused much on documentation—who does, knowledge lived in our heads mostly. But when we invited an external team to review our &lt;strong&gt;Nx&lt;/strong&gt; workspace, we had to actually write things down. At first, it felt like busywork, but it clarified our reasoning, exposed gaps, and suddenly onboarding a new dev didn’t seem impossible. What started as “just for the reviewer” ended up being for us: documentation became a tool for thinking.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code That Could Stay Put
&lt;/h2&gt;

&lt;p&gt;Not everything needs to be migrated. A useful lens is to ask: &lt;em&gt;Is it valuable, used often, and under our control?&lt;/em&gt; If not, it might be better left alone.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short-lived or legacy code&lt;/strong&gt; — End-of-life systems, POCs, and prototypes are rarely worth the effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-impact tools&lt;/strong&gt; — Admin dashboards, internal scripts, or utilities used by a handful of people could be evaluated case-by-case and categorised as low-priority to be done after the main goals have been achieved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stable or performance-critical modules&lt;/strong&gt; — For code that’s rock-solid or finely tuned for performance, don’t prioritize moving it just for the sake of uniformity. Wait until change is truly needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archived or compliance projects&lt;/strong&gt; — Kept only for historical or legal reasons, these don’t need to me moved.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Side Effects We Actually Liked
&lt;/h2&gt;

&lt;p&gt;On top of the main goals, there were a few extra benefits made the year even sweeter…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero version drama&lt;/strong&gt; – Honestly, we hadn't thought about keeping the workspace up to date during the migration, but even before all apps were fully migrated, we were running workspace-wide updates between app migrations. By using &lt;a href="https://nx.dev/reference/core-api/nx/documents/migrate" rel="noopener noreferrer"&gt;&lt;code&gt;nx migrate&lt;/code&gt;&lt;/a&gt;, Nx auto-updated all its dependencies for us. Genuis!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smoother developer experience&lt;/strong&gt; – With shared libraries, single source of truth, and consolidated utilities, knowledge sharing became easier. Tracking components or utilities was simpler, and both local development and onboarding became more predictable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviews that matter&lt;/strong&gt; – Shared modules are tested rigorously, so we no longer need to review every utility implementation. Code reviews focus on feature logic instead of rehashing the same patterns, a big improvement over the old polyrepo setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team Trust in Action -&lt;/strong&gt; Full disclosure: at first we didn't have &lt;code&gt;default&lt;/code&gt; branch protection policies in place, not because we wanted to avoid code-reviews, we just couldn't get the GitHub rules sets to behave the way we wanted. We still practiced code reviews, they just weren't enforced. It’s not perfect, but it worked for our small, trusting team—and it shows there’s room to grow.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;A year into our monorepo migration, a few key lessons stand out—lessons that helped us keep the project moving while learning along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Start Smart&lt;/strong&gt;&lt;br&gt;
Before touching production, we spun up many test workspaces and pipelines. Experimenting early gave us confidence and reduced “gotchas.” At the same time, establishing clear boundaries—deciding which apps or modules to migrate and what fell in our mandate—kept the work manageable. Nx generators like &lt;code&gt;@nx/workspace:move&lt;/code&gt; made it easy to adjust structure later, so we didn’t need to get everything perfect from day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Move in Steps&lt;/strong&gt;&lt;br&gt;
Migrating one app at a time allowed us to validate our setup and refine tooling before tackling bigger projects. Each app became a learning opportunity rather than a risk. We also learned to accept that first-pass solutions wouldn’t cover every edge case. Some duplication or temporary shortcuts were inevitable, but planning to refine incrementally kept momentum going without compromising quality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Keep It Practical&lt;/strong&gt;&lt;br&gt;
It’s tempting to overengineer: abstract everything, customize every build, and aim for perfection. In reality, sticking to practical choices—leaning on defaults, balancing abstraction, and making deliberate trade-offs—kept progress moving. Sometimes duplicating a component temporarily was faster, with the intent to refactor later once all use cases were understood. Pragmatic decisions lead to reusable code without slowing development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Document as You Go&lt;/strong&gt;&lt;br&gt;
Writing down assumptions, documenting shared components, utilities, and conventions clarified our thinking and improved onboarding. Initially, it felt like busywork for an external reviewer, but it quickly became an internal tool for tracking decisions, spotting gaps, and making the migration more maintainable. Good documentation isn’t just for others—it reinforces clarity for your own team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Clean as You Go&lt;/strong&gt;&lt;br&gt;
Unexpected quirks, outdated dependencies, and long-forgotten scripts surfaced repeatedly. Each one became an opportunity to clean up and standardize. Tackling these early prevented bigger problems down the line and improved overall code quality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Build Confidence&lt;/strong&gt;&lt;br&gt;
Rigorous testing in shared modules turned out to be a huge time-saver. With solid test coverage, we no longer needed to review every utility implementation, and refactoring or updating apps felt safer. Testing wasn’t just about catching bugs—it reinforced trust in the shared code and the migration as a whole.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Taken together, these lessons show that a thoughtful, incremental approach—prioritizing practicality, documentation, and testing—makes a monorepo migration manageable, educational, and ultimately rewarding.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;One year in, the monorepo has delivered more than just technical improvements—it has transformed how we work. Getting here wasn’t simple. Each app migration involved careful planning, rethinking abstractions, and multiple iterations as new requirements and edge cases emerged. There were moments of trial and error, but every challenge taught us something valuable.&lt;/p&gt;

&lt;p&gt;Today, apps are consistent, upgrades are simpler, and bootstrapping new applications is faster thanks to shared layouts, utilities, and workflows. CI/CD automation still has room to grow, onboarding could be smoother, and shared libraries will keep evolving—but the foundation is solid, and day-to-day development is now more predictable and less error-prone.&lt;/p&gt;

&lt;p&gt;For small teams, the lessons are clear: embrace an incremental migration, prioritize practical reuse over perfection, and experiment and &lt;em&gt;leverage&lt;/em&gt; tools like &lt;a href="https://nx.dev/?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Nx&lt;/a&gt; to make things easier for you. The monorepo isn’t just a technical shift—it’s a cultural one, and the effort has been well worth it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One repo, many lessons, endless possibilities.&lt;/strong&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Supercharging Your Workflows with Local GitHub Actions</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Fri, 29 Aug 2025 19:01:52 +0000</pubDate>
      <link>https://forem.com/codenamegrant/supercharging-your-workflows-with-local-github-actions-2o23</link>
      <guid>https://forem.com/codenamegrant/supercharging-your-workflows-with-local-github-actions-2o23</guid>
      <description>&lt;p&gt;If you’ve worked with GitHub Actions before, you’ve probably pulled in a bunch of ready-made steps from the &lt;a href="https://github.com/marketplace?type=actions" rel="noopener noreferrer"&gt;Marketplace&lt;/a&gt;. That’s awesome for common needs like setting up Node, caching dependencies, or deploying to cloud providers.&lt;/p&gt;

&lt;p&gt;But what about those repetitive, project-specific steps that aren’t worth publishing to the whole world? That’s where &lt;strong&gt;local GitHub Actions&lt;/strong&gt; shine.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are Local Actions?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;local action&lt;/strong&gt; is a custom GitHub Action that lives &lt;em&gt;inside your own repository&lt;/em&gt;. Unlike Marketplace Actions, they’re not shared publicly—just tailored helpers your workflows can use again and again.&lt;/p&gt;

&lt;p&gt;They live under the special &lt;code&gt;.github/actions/&lt;/code&gt; directory in your repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.github/   
  actions/
    bootstrap-node/
      README.md (optional)
      action.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, you can call them in any workflow as if they were published actions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo: Bootstrap Node Environment
&lt;/h2&gt;

&lt;p&gt;Let’s say multiple workflows in your project need the same basic setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Setup Node.js&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npm ci&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of repeating that block everywhere (and making sure they all use Node v20 and cache npm correctly), you create a local action:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;.github/actions/bootstrap-node/action.yml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bootstrap&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Node&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Environment'&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Sets&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;up&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Node.js&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;20)&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;optionally&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;installs&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dependencies'&lt;/span&gt;

&lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;installDeps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Whether&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;install&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dependencies'&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;

&lt;span class="na"&gt;runs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;using&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;composite'&lt;/span&gt;

  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
        &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.installDeps == 'true' }}&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Usage in a workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./.github/actions/bootstrap-node&lt;/span&gt;

      &lt;span class="c1"&gt;# Node environemnt is ready to use&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every workflow that needs a Node environment can use this clean one-liner instead of duplicating setup code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Exciting Use Cases for Local GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Local actions are great for &lt;strong&gt;project-specific tasks&lt;/strong&gt; that you’d otherwise duplicate across multiple workflows. Some examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Monorepo Dependency Filtering&lt;/strong&gt; – Only run jobs for apps/libs that actually changed.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable Security Checks&lt;/strong&gt; – Centralize audits (npm, yarn, trivy) into one action.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Changelog Generation&lt;/strong&gt; – Auto-generate and update &lt;code&gt;CHANGELOG.md&lt;/code&gt; from commits.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack/Teams Notifications&lt;/strong&gt; – Send consistent, branded build/deploy alerts.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Preview Environments&lt;/strong&gt; – Spin up ephemeral environments (e.g., Vercel/Netlify/containers) for each PR.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardized Code Quality Gates&lt;/strong&gt; – Bundle linting, formatting, and type-checks into one action for consistency across workflows.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a small (for now) &lt;a href="https://github.com/CodeNameGrant/local-action-toolbox" rel="noopener noreferrer"&gt;local-action-toolbox&lt;/a&gt;, that has some demos.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Use Local Actions?
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;DRY (Don’t Repeat Yourself):&lt;/strong&gt; No more copy-pasting steps.&lt;br&gt;
✅ &lt;strong&gt;Clarity:&lt;/strong&gt; Workflows are more streamlined and readable.&lt;br&gt;
✅ &lt;strong&gt;Flexibility:&lt;/strong&gt; Inputs let you toggle behavior (like &lt;code&gt;installDeps: false&lt;/code&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrap-Up
&lt;/h2&gt;

&lt;p&gt;Local GitHub Actions are a small investment that can pay off in a big way. They help keep workflows clean, reduce copy-paste drift, and turn repetitive steps into simple, reusable building blocks.&lt;/p&gt;

&lt;p&gt;So the next time you spot the same steps repeated across multiple workflows, think about wrapping them into a local action. It’s one of those moves your future self—and your teammates—will definitely appreciate.&lt;/p&gt;

</description>
      <category>githubactions</category>
    </item>
    <item>
      <title>Link Pull Requests to JIRA Issues with Ease</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Thu, 28 Aug 2025 18:18:30 +0000</pubDate>
      <link>https://forem.com/codenamegrant/link-pull-requests-to-jira-issues-with-ease-22ae</link>
      <guid>https://forem.com/codenamegrant/link-pull-requests-to-jira-issues-with-ease-22ae</guid>
      <description>&lt;p&gt;Linking GitHub pull requests to JIRA tickets can be irritating. PR descriptions often miss references to the right ticket, or contributors forget to include links altogether. This makes it harder for reviewers and project managers to connect code changes to the actual work item.&lt;/p&gt;

&lt;p&gt;The JIRA PR Link Action can solve that problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 What It Does
&lt;/h2&gt;

&lt;p&gt;This GitHub Action automatically adds a “Link to JIRA ticket” line to your pull request description whenever it finds a valid JIRA key in the PR title.&lt;/p&gt;

&lt;p&gt;No more manual setting up Markdown links, as long as you include the ticket in the title, it will be prepended or appended as a link to your PR body.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fjnk9xl09pghfe6s4hwzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fjnk9xl09pghfe6s4hwzo.png" alt="Demo Image" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistent Documentation:&lt;/strong&gt; Every pull request gets a properly formatted JIRA link.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved Collaboration:&lt;/strong&gt; Reviewers and PMs can jump straight from GitHub to the relevant JIRA issue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Save Time:&lt;/strong&gt; Contributors don’t need to remember to copy/paste ticket URLs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CI/CD Ready:&lt;/strong&gt; Fits neatly into any PR workflow without extra setup.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚡ How to Use It
&lt;/h2&gt;

&lt;p&gt;Add the action to your workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Add JIRA Links&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;edited&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;reopened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;add-jira-links&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codenamegrant/jira-pr-link-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;github-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;jira-base-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://yourcompany.atlassian.net'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. From now on, PRs will automatically include links to their JIRA tickets.&lt;/p&gt;

&lt;h2&gt;
  
  
  👉 Try it now on your repos: &lt;a href="https://github.com/marketplace/actions/jira-link-action" rel="noopener noreferrer"&gt;JIRA Link Action&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>github</category>
      <category>automation</category>
      <category>productivity</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Find Your Drift (Bolt.new Hackathon)</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Thu, 19 Jun 2025 17:48:33 +0000</pubDate>
      <link>https://forem.com/codenamegrant/find-your-drift-boltnew-hackathon-3g21</link>
      <guid>https://forem.com/codenamegrant/find-your-drift-boltnew-hackathon-3g21</guid>
      <description>&lt;h3&gt;
  
  
  A visual debt-tracking and simulation app — built &lt;del&gt;with&lt;/del&gt; by AI.
&lt;/h3&gt;

&lt;p&gt;Debt isn’t static. Interest rates change. Payments drift. You put money in, you take money out—and suddenly your loan looks nothing like the one you started with. Most debt tools show you a snapshot—one number, one plan. But real debt drifts.I built Drift to help make that visible.&lt;br&gt;
It’s a tool for seeing your debt clearly, tracking how it evolves, and simulating different ways to move forward.&lt;/p&gt;

&lt;p&gt;That’s why I built &lt;strong&gt;&lt;a href="https://findyourdrift.money/" rel="noopener noreferrer"&gt;Drift&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 What is Drift?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Drift&lt;/strong&gt; is a finance productivity app that helps you see your debt clearly and understand how it changes over time. It's built to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What does my bond/morgage look different this month?&lt;/li&gt;
&lt;li&gt;What if I skip a payment—or make an extra one?&lt;/li&gt;
&lt;li&gt;What happens if the prime rate shifts by 0.5%?&lt;/li&gt;
&lt;li&gt;How much sooner could I finish paying off my loan?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It gives you one place to visualize and simulate your entire debt portfolio.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚙️ Built With AI (and vibes)
&lt;/h3&gt;

&lt;p&gt;I built Drift as part of a the &lt;a href="https://worldslargesthackathon.devpost.com/" rel="noopener noreferrer"&gt;Worlds Largest Hackaton&lt;/a&gt; using &lt;a href="https://bolt.new" rel="noopener noreferrer"&gt;Bolt.new&lt;/a&gt;, a platform that turns prompts into working applications. I leaned into the AI-heavy approach—not micromanaging the implementation, but instead focusing on clearly expressing the goal. Sometimes I was even deliberately vague just to see what initiative the AI would take.&lt;/p&gt;

&lt;p&gt;The result? A prototype that’s imperfect but real. And most importantly: useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 My Goal
&lt;/h3&gt;

&lt;p&gt;This wasn’t just a test of product ideas—it was more a test of process. Could I hand over most of the deveopment to AI, and still ship something real?&lt;/p&gt;

&lt;p&gt;I used Bolt.new, gave it vague but intentional prompts, and tried to act more like a director than a developer (which is quite hard as a technical person). I didn’t stress about clean code or best practices (OMG, even harder). I focused on outcomes. The goal wasn’t to craft a scalable system—it was to see if I could turn a complex financial concept into something usable, quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧪 What I've Learned So Far
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;Letting go is hard (and valuable)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;I’m used to shaping every detail of a codebase. Letting AI make decisions—naming things, choosing structure, even picking libraries—felt unnatural. But once I stopped resisting, the project moved faster. The code wasn't always pretty (hell, it still isnt pretty), but the outcome was.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;Clarity of intent is everything&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The more clearly I could describe what I wanted the app to &lt;em&gt;do&lt;/em&gt;, the better the AI delivered. I didn’t need to micromanage. I needed to explain my goal like I would to a teammate. That was a shift: less about specs, more about &lt;em&gt;vision&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But even when I wasn’t clear, Bolt.new had my back. Its “Enhance Prompt” feature let me turn stream-of-consciousness brain dumps into structured, actionable instructions. I could write like I think—and let the AI clean it up. That lowered the barrier to trying ideas quickly without slowing down to get the phrasing perfect.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;You can build something real without building it "right"&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;I didn’t optimize, architect, or even test properly. I just shipped. And what I shipped does something useful. That's liberating—and a reminder that polish can come later.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. &lt;strong&gt;Debt is weird and personal&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Modeling debt isn't just numbers. It’s behaviors. Skipped months, random withdrawals, inconsistent rates. Even just visualizing that mess makes a difference—it makes people feel more in control of something that is usually invisible.&lt;/p&gt;




&lt;h3&gt;
  
  
  🌱 What’s Next for Drift
&lt;/h3&gt;

&lt;p&gt;Drift is still in development. Right now, it allows users to simulate a single loan in a static way—great for testing scenarios, but just the beginning.&lt;/p&gt;

&lt;p&gt;Here’s what’s coming next:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User profiles &amp;amp; multi-account support:&lt;/strong&gt; I’m currently working on the ability to create a personal profile and track multiple debt accounts of different types—bonds, credit cards, student loans, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-account simulations:&lt;/strong&gt; Once account tracking is in place, I’ll expand simulations so you can test “what if” scenarios on each loan independently, with custom settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-powered repayment strategies:&lt;/strong&gt; I plan to integrate AI to suggest tailored repayment approaches based on your loan profile and goals—minimize interest, finish faster, or free up cash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Future ideas&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Long term, I’d love to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track &lt;strong&gt;prime interest rate changes&lt;/strong&gt; automatically &lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;“partner mode”&lt;/strong&gt; so you can plan with a spouse or business partner&lt;/li&gt;
&lt;li&gt;Explore &lt;strong&gt;bank integrations&lt;/strong&gt; for live data and auto-tracking &lt;em&gt;(who knows—maybe even budgeting or forecasting tools)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💬 Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Drift is still early—but it’s already useful. It takes debt out of the shadows and puts it on a canvas. You can tweak, simulate, compare. You can &lt;em&gt;see&lt;/em&gt; your drift—and once you can see it, you can start steering it.&lt;/p&gt;

&lt;p&gt;This was built for a hackathon, but I hope it becomes something more.&lt;/p&gt;

&lt;p&gt;Thanks for reading — and if you’ve ever felt overwhelmed by the messiness of debt, I hope Drift gives you a little bit of clarity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://findyourdrift.money/" rel="noopener noreferrer"&gt;Find Your Drift&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>promptengineering</category>
      <category>hackathon</category>
      <category>fintec</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Monorepos: Proposing a Monorepo to your team (Q&amp;A)</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Thu, 13 Jun 2024 09:01:26 +0000</pubDate>
      <link>https://forem.com/codenamegrant/monorepos-2dj</link>
      <guid>https://forem.com/codenamegrant/monorepos-2dj</guid>
      <description>&lt;p&gt;This post follows a demo I did for my team leaders, where I explained what a monorepo is and how it could address the problems we are currently facing with web frontend dev.&lt;br&gt;
The team leaders include a mix of senior developers and business analysists. So some questions are more technical and some are more general. The questions are in no particular order.&lt;/p&gt;

&lt;p&gt;
  How would a monorepo address the issue of one app being dependent on components from another, potentially causing coupling and making updates to the dependent app more difficult?
  &lt;br&gt;
There shouldn't be a scenario where apps are dependent on another. Application modules should be a minimal as possible and import feature libraries where all the heavy lifting is done. The feature, which exists outside the application, in a shared space, can then be imported into different applications.&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;
  How do you decide what's shared and what's not? "&lt;em&gt;I don’t want anyone using my stuff&lt;/em&gt;"
  &lt;br&gt;
Some components and utilities will be obviously shared, like authentication or common UI components (like navigation, form elements, etc), date management tools or constants. Other components will have to be evaluated as the apps are built.&lt;br&gt;
This will require some thought before hand, &lt;em&gt;What components do we have in our separate apps now that need to be shared? What kind of components are they? Are they presentational or transactional? Are any written for a single use case but&lt;/em&gt; can &lt;em&gt;be made generic?&lt;/em&gt; &lt;br&gt;
As developers we will have to gauged and decide on a way to pre-emptively decide what should be created as a shared component from the get go. For example if you are creating controls or navigation elements those should probably be shared.&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;
  How can a monorepo manage the potential issues arising from poor developer communication and lack of discipline in adhering to pre-agreed rules when using shared or app-specific code?
  &lt;br&gt;
There are a number of methods both manual an automated that can improve on this issue:

&lt;ol&gt;
&lt;li&gt;Create clear documentation outlining best practices and expected standards and conventions.&lt;/li&gt;
&lt;li&gt;Enforce Code Reviews and PR's by multiple team members to ensure team members adhere to the guidelines. This may sound heavy handed, but will prevent bad practices from spinning out of control.&lt;/li&gt;
&lt;li&gt;Module Architecture: Organize the codebase in a modular fashion, where shared components and app-specific code are clearly separated.&lt;/li&gt;
&lt;li&gt;Automated Testing and CI: Setup automated testing to catch issues early and use CI pipelines to run linting checks on PRs.&lt;/li&gt;
&lt;li&gt;Training and Onboarding: Provide training sessions for new developers to familiarize them with the monorepo, tools and best practices.&lt;/li&gt;
&lt;li&gt;Regular update meetings: Schedule regular meetings to discuss ongoing work, updates and issues that team members are having with the monorepo. Foster a culture of open communication where developers feel comfortable discussing potential problems and proposing solutions
&lt;/li&gt;
&lt;/ol&gt;




&lt;/p&gt;
&lt;p&gt;
  How does a monorepo prevent issues seen in previous applications, where a lack of documentation and dependency visibility leads developers to create multiple similar routines instead of modifying exiting ones?
  &lt;br&gt;
Monorepo tools in general provide a lot more support to the architecture than legacy apps, including dependency visibility. Informing developers what effects the changes they make will have and where&lt;br&gt;


&lt;br&gt;

  How does a monorepo handle the need to test a shared dependency across all apps that consume it when updates are made?
  &lt;br&gt;
Any consuming apps would have to under go testing in some form or another to confirm their goals are still being achieved. However much of that testing can be automated by impl unit test or e2e testing with Storybook or Cypress. If you automate the boilerplate testing (eg CRUD), manual testing might only have to be done for edge case scenarios.&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;
  How does a monorepo address concerns that updating shared libraries forces all dependent apps to be updated simultaneously, compared to the current approach where libraries can be updated per project, allowing failures to be managed in a smaller, more controlled environment?
  &lt;br&gt;
Some monorepo tools do support a repo-wide set of dependencies that can be overridden on a per-app/lib basis, however this approach should carefully analysed as it can lead to each app having their own list of deps instead of inherit a list of shared deps and then you are back in dependency hell.&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;
  How do we know that by using a monorepo, we arent swapping one set of problems for another? There needs to be more research into the pros/cons from teams that have walked thsi road.
  &lt;br&gt;
There are many articles that talk about how a monorepo saved them from the same problems that we are facing. It obviously is not a silver bullet and they described challenges and limitations they had to cater for and overcome.&lt;br&gt;
Just like there are articles that promote the joys of monorepos there are also a few that discourage its usefulness, and nothing sparks a debate in the software community like posting articles that a particular subject is the absolute worst. This lead me to the comment sections which yielded what seems to be the most honest impressions of monorepos:

&lt;ul&gt;
&lt;li&gt;A monorepo doesn't have to be the one-repo-to-rule-them-all, it can also mean on repo for related set of services or applications. Monorepos shouldn't be used to encompass an entire company's codebase unless like Google, Facebook (and others) you also have the engineering resources to do so.&lt;/li&gt;
&lt;li&gt;Monorepos can produce complications when scaling, but general consensus is that this is only an issue if you have dozens of developers committing daily to a project with hundreds or thousands of lines of code.&lt;/li&gt;
&lt;li&gt;Monorepos do have challenges, its not disputed, but they can be overcome and the benefits are worth it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sources at the end of the post&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;
  What is the breaking point for moving away from our current path?
  &lt;br&gt;
Continuing our current approach will just exacerbate the problem (of code duplication and dependency hell), leading to the product suite as a whole becoming untenable. We shouldn't wait for the breaking point though before acting. We know there is a problem now, we should begin to proactively resolve it while we have the resources do so. Waiting for a breaking point could mean that when we do address it, we are under time constraints that don't allow adequate investigation or experimentation to impl a new tool correctly.&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;
  Is there and automated tool that can migrate our apps into a monorepo and identify the common code that needs to be made sharable?
  &lt;br&gt;
There are tools to import a standalone repo into a monorepo structure but to conduct code analysis to determine shared/duplicate code would have to be done using a separate tool. This could save time on the migration but it would add the complexity of having to learn and configure these tools for a one-time use. If we had many more repos it could be viable, but I dont think it would add value when migrating the 5/6 we have.&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;
  How does using a monorepo change testing by QC of different apps that depend on the same feature change in different environments? Both apps are built together, so it would seem that they are dependent on each other?
  &lt;br&gt;
Remember that a monorepo does not have to deploy a single build artifact. Each app can result in its own build artifact thats published and deployed independently. So if one of the two artifacts are deployed and testing finds a bug. The other artifact can just carry on.&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;
  How does a monorepo address concerns regarding read access to the codebase, particularly in the context of hiring an intern, where specific repo/project access is typically granted?
  &lt;br&gt;
This is not a problem that monorepo tools tackle. This would be a GitHub issue, and it does not support sub-directory read-only access.&lt;br&gt;
So to answer your question, there is no solution, read access is all-or-nothing.&lt;br&gt;
This would raise concerns if we were applying the methodology to multiple parts of the business (ie. web-clients, mobile-clients, microservices, etc), but since we are only targeting the web-clients (which are so similar that access to one means you can guess whats in the others) it shouldn't be a concern.&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Posts about experience with monorepos&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/empathyco/moving-to-a-mono-repo-part-1-the-journey-eb63efd8ef64" rel="noopener noreferrer"&gt;Moving to a Monorepo Part 1 - The Journey&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/empathyco/moving-to-a-mono-repo-part-2-the-destination-a47e597ff50d" rel="noopener noreferrer"&gt;Moving to a Monorepo Part 2 - The Destination&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techblog.shippio.io/moving-to-a-mono-repo-f9e12f3f7c84" rel="noopener noreferrer"&gt;Moving to a Mono-repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/ing-blog/two-years-of-monorepo-hand-on-experience-47246b89a50a" rel="noopener noreferrer"&gt;Two years of monorepo hand-on experience&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://betterprogramming.pub/journey-of-a-frontend-monorepo-what-i-learned-d6a0d142803f" rel="noopener noreferrer"&gt;Journey of a Frontend Monorepo: Here’s What I Learned&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alexey-soshin.medium.com/monorepo-is-a-bad-idea-5e587e848a07" rel="noopener noreferrer"&gt;Monorepo is a bad idea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b" rel="noopener noreferrer"&gt;Monorepos: Please don’t!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@cxmcc/thanks-for-sharing-i-had-a-very-similar-experience-the-pain-points-are-so-similar-ddf1b37f156c" rel="noopener noreferrer"&gt;Thanks for sharing. I had a very similar experience.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>learning</category>
      <category>architecture</category>
      <category>software</category>
      <category>monorepo</category>
    </item>
    <item>
      <title>Monorepos: Making Git Blame a Family Affair</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Wed, 12 Jun 2024 06:13:09 +0000</pubDate>
      <link>https://forem.com/codenamegrant/monorepos-making-git-blame-a-family-affair-1lc7</link>
      <guid>https://forem.com/codenamegrant/monorepos-making-git-blame-a-family-affair-1lc7</guid>
      <description>&lt;p&gt;Monorepos have gained a lot of popularity recently, especially among web developers. There are many resources that cover this topic, so I will briefly cover What is a monorepo and the benefits it provides. At the end I will cite the resources I used to get to this point.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a monorepo?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A monorepo is a single repository containing multiple distinct projects, with well-defined relationships. - &lt;a href="https://monorepo.tools/#what-is-a-monorepo" rel="noopener noreferrer"&gt;Monorepo.tools&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But a monorepo is about more than just code co-location because without the well defined relationships or boundaries, a monorepo can quickly become monolithic. And a good monorepo is the opposite of monolithic (&lt;a href="https://blog.nrwl.io/misconceptions-about-monorepos-monorepo-monolith-df1250d4b03c" rel="noopener noreferrer"&gt;Misconceptions about Monorepos: Monorepo != Monolith&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a Monorepo?
&lt;/h2&gt;

&lt;p&gt;The opposite of a monorepo is a polyrepo or multirepo; that is a new repo for each project, i.e.. "1 repo = 1 build artifact". This approach is popular because it encourages team/developer autonomy. Developers can dictate their own tech stack, testing strategies, deployment, and (most importantly to them) who can contribute to their codebase.&lt;/p&gt;

&lt;p&gt;Now those are all good things, but there are drawbacks to this multi-repo approach that most developers are aware of, but put to down to 'that’s just how it is'. Drawbacks like cumbersome code sharing, significant code duplication and inconsistent practices, cross-repo collaboration problems, inconsistent tooling, dependency hell when updating, automation difficulties, scalability issues, and so on.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"... this autonomy is provided by isolation, and isolation harms collaboration" - &lt;a href="https://monorepo.tools/#polyrepo-concept" rel="noopener noreferrer"&gt;Monorepo.tools&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;So how is a monorepo any better?&lt;/strong&gt;&lt;br&gt;
For starters, if all your related code is co-located, its much easier to manage dependency updates across all apps, the same shared libraries can be made available to all relevant apps and provide a single source of the truth. Tooling and coding standards can be enforced more consistently. Developers can work across different projects or components more seamlessly, facilitating better teamwork and knowledge sharing. Changes or refactoring that affect multiple components can be made in a single PR, ensuring that all related changes are coordinated and reducing the risk of breaking changes.&lt;/p&gt;

&lt;p&gt;While monorepos offer these (and more) benefits, they also come with their own challenges, such as potential scalability issues with very large code bases, increased build times, the need for more sophisticated tooling to manage the codebase's complexity. However, many organizations find that the advantages of a monorepo outweigh the disadvantages, particularly in terms of improving collaboration, consistency, and overall code quality.&lt;/p&gt;
&lt;h2&gt;
  
  
  Misconceptions
&lt;/h2&gt;

&lt;p&gt;Misconceptions about monorepos (or any tool or technology) often deter teams from adopting this development approach. Lets shed some light on a few and expose the reality of using a monorepo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monorepos should contain all a company's code&lt;/strong&gt;&lt;br&gt;
While large companies like Google or Facebook practice this behaviour it is not the norm. Many teams will only group very similar, closely related apps that can benefit from a shared workspace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monorepos mean all projects must be tightly coupled&lt;/strong&gt;&lt;br&gt;
Monorepos can still support modularity and separation of concerns. In fact many monorepo management tools will advocate for decoupling components and maintaining an modular architecture, otherwise the monorepo is just monolithic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monorepos lead to longer build times and slower CI/CD pipelines&lt;/strong&gt;&lt;br&gt;
With proper tooling and optimization, such as incremental builds and distributed CI/CD systems, build times can be managed effectively. Tools like Nx, Bazel &amp;amp; Lerna utilize incremental builds to only build affected components and caching to prevent building the same thing twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monorepos are harder to scale&lt;/strong&gt;&lt;br&gt;
While monorepos do present unique scaling challenges, they also offer solutions for consistent and scalable development practices. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monorepos are harder to secure&lt;/strong&gt;&lt;br&gt;
Security in monorepos can be effectively managed with proper practices. By only including related projects in a monorepo along with access controls, code reviews and &lt;a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners" rel="noopener noreferrer"&gt;CODEOWNERS&lt;/a&gt; can ensure that the codebase remains secure.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Real Challenges
&lt;/h2&gt;

&lt;p&gt;Just because the many misconceptions are manageable, does not mean monorepos don't have challenges of their own, like migration complexity, tooling adjustments, and workflow changes. Proper planning and best practices are essential to navigate these hurdles and successfully transition to a unified codebase. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Migration Complexity:&lt;/strong&gt;
Moving code from multiple repos into a single repo while ensuring dependencies are mapped and managed correctly can be complex, especially if projects have different histories, dependencies and configurations. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooling and infrastructure:&lt;/strong&gt;
Not all your existing build systems, CI/CD pipelines or development tools are designed to handle large monorepos and may require significant updates or replacements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development Workflow Changes:&lt;/strong&gt;
Ensuring the effectiveness of code reviews in a larger, more integrated codebase and adapting or redefining branching strategies to suit the monorepo setup while maintaining workflow efficiency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cultural and Organizational Resistance:&lt;/strong&gt;
Convincing a team that use to working independently to adopt to the new approach and workflow can be challenging. Resistance is common and training may be required while they adjust to the more integrated and collaborative development environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency and Maintenance:&lt;/strong&gt;
Maintaining consistent coding standards, practices and docs will need to be applied across the repo. Managing and addressing technical debt will be more critical as the monorepo grows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning Curve:&lt;/strong&gt;
New team members may require additional training to understand the structure, tools, processes and workflows of monorepo management and development.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Final Thoughts?
&lt;/h2&gt;

&lt;p&gt;Monorepos are not a silver bullet. And there will be challenges and trade-offs and a culture shift and training and yes, it will be a lot of work. But the benefits could far outweigh those challenges; less time worrying about boilerplate and config per project leads to faster turnaround time on new features or bug fixes, easier maintenance leads to faster adaption of the newer technology trends or tech stacks, more test coverage leads to a more stable product and better code quality which in turn leads to easier adaption by new developers and easier dependency maintenance and more.&lt;/p&gt;

&lt;p&gt;
  Experiences with negative Monorepo research/posts
  &lt;br&gt;
In my research I did find posts that describe negative experiences with monorepos. Posts that talk about how the benefits of monorepos are not so beneficial and that anyone using a monorepo should abandon all hope. However, nothing stirs up a the software community like telling them that a technology they are using is just the worst. So it’s the comment sections of these post that are more valuable than the articles, with developers denouncing the article and coming to the defence of monorepos, posting how they were able to overcome, mitigate or avoid the problems that the original author encountered.&lt;br&gt;


&lt;/p&gt;


&lt;p&gt;&lt;br&gt;
  Sources&lt;br&gt;
  &lt;ul&gt;

&lt;li&gt;&lt;a href="https://monorepo.tools/" rel="noopener noreferrer"&gt;monorepo.tools&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://blog.nrwl.io/misconceptions-about-monorepos-monorepo-monolith-df1250d4b03c" rel="noopener noreferrer"&gt;Misconceptions about Monorepos: Monorepo != Monolith&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=9iU_IE6vnJ8&amp;amp;ab_channel=Fireship" rel="noopener noreferrer"&gt;Monorepos - How the Pros Scale Huge Software Projects // Turborepo vs Nx&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=_iqcMdEOrF4&amp;amp;ab_channel=NDCConferences" rel="noopener noreferrer"&gt;Monorepo – How to do frontend faster, better and safer - Kari Meling Johannessen - NDC Oslo 2023&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=15VeTQLnWrs" rel="noopener noreferrer"&gt;Monorepos - The Benefits, Challenges and Importance of Tooling Support by Juri Strumpflohner&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://lembergsolutions.com/blog/why-you-should-use-monorepo-and-why-you-shouldnt" rel="noopener noreferrer"&gt;Why You Should Use a Monorepo (and Why You Shouldn’t)&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://betterprogramming.pub/the-pros-and-cons-monorepos-explained-f86c998392e1" rel="noopener noreferrer"&gt;The Pros and Cons of Monorepos, Explained&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://how-to.dev/pros-and-cons-of-keeping-your-code-in-monorepo" rel="noopener noreferrer"&gt;Pros and Cons of Keeping Your Code in Monorepo&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://www.toptal.com/front-end/guide-to-monorepos" rel="noopener noreferrer"&gt;Guide to Monorepos for Front-end Code&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://circleci.com/blog/monorepo-dev-practices/" rel="noopener noreferrer"&gt;Benefits and challenges of monorepo development practices&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://semaphoreci.com/blog/what-is-monorepo" rel="noopener noreferrer"&gt;What is monorepo? (and should you use it?)&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;
&lt;br&gt;
&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>architecture</category>
      <category>learning</category>
    </item>
    <item>
      <title>Monorepos: From Discovery to Deployment - A Developer's Journey</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Mon, 05 Feb 2024 11:48:48 +0000</pubDate>
      <link>https://forem.com/codenamegrant/monorepos-from-discovery-to-deployment-a-developers-journey-d9o</link>
      <guid>https://forem.com/codenamegrant/monorepos-from-discovery-to-deployment-a-developers-journey-d9o</guid>
      <description>&lt;p&gt;My first introduction to the concept of a monorepo came when I was search for an alternative to Create-React-App. One option that had potential and a lot of support was &lt;a href="https://nx.dev" rel="noopener noreferrer"&gt;Nx&lt;/a&gt;, which was (and still is) described and a build system with "First Class support for monorepos". I didn't fully understand the concept of a monorepo at the time beyond that "mono" meant one and "repo" obviously referred to a GitHub repo. So it fell by the wayside in my mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  A New Problem
&lt;/h2&gt;

&lt;p&gt;A few months later, I started investigating a new problem my team and I were struggling with. For some context, my team and I manage the frontend of product suite. And like many product suites, the webapps follow the same design system and make use of many similar components. Even "under the hood" they all have the same tech stack.&lt;/p&gt;

&lt;p&gt;The problem with this methodology (as I'm sure you have realized) is that of duplication. This problem of duplication can be split into 3 parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dependency maintenance must be repeated on each webapp separately and then tested and deployed&lt;/li&gt;
&lt;li&gt;It’s too much of a hassle to maintain code sharing libraries, "It’s just easier to implement it myself", leading to duplicated code.&lt;/li&gt;
&lt;li&gt;Creating new webapps within the suite results in another identical project that adds to the above problems&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Solution Requirements
&lt;/h3&gt;

&lt;p&gt;The solution to this problem is to implement a methodology that makes dependencies and other shared code more accessible to multiple features. This solution would also need to be extendable, so that any new features can inherit any already existing boilerplate code or shared components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research
&lt;/h2&gt;

&lt;p&gt;And so the research began... ooh, that sounds to episodic. I explored a number of popular options including; shared config modules, project templates, code generators and shared git submodules are all fine recommendations. But none of them were able to fulfill all the requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monorepos&lt;/strong&gt; though, practice consolidating all apps and libraries in one workspace/codebase/repository, simplifying dependency management and ensuring code consistency. With the shared components in one place, representing a single source of truth, any updates to them are synchronized across all apps and libraries. Any new apps will also inherit existing project configurations thereby streamlining development and maintaining code coherence. &lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Get Going
&lt;/h2&gt;

&lt;p&gt;Hold up now, &lt;em&gt;"One does not simply implement a monorepo"&lt;/em&gt;. This is a fundamental change to your development architecture and has more far reaching effects than just solving this problem I've described. Each aspect of how you want to implement a monorepo needs to be investigate and tested, proof of concepts should be built to demonstrate that the tools or techniques you want to use are beneficial to your product and team. I say this not to dissuade you from starting, but to make sure each aspect is implemented deliberately, because once you start down the road of a new methodology, it can be harder to backtrack if you encounter issues.&lt;/p&gt;

&lt;p&gt;Additionally, having a development plan can help to sell the idea to other team members, leaders and most importantly, sponsors.&lt;/p&gt;

&lt;p&gt;This series will cover my journey of migrating from a multi-repo to a monorepo architecture, as well as the research I did, questions I asked and challenges I faced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up next
&lt;/h2&gt;

&lt;p&gt;The next part will cover a short intro of what monorepos are, pros and cons, features and challenges. I'll also include a few Q&amp;amp;As from my team about this approach.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>softwaredevelopment</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Multiple GraphQL Endpoints with Apollo Client</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Tue, 09 Jan 2024 09:46:06 +0000</pubDate>
      <link>https://forem.com/codenamegrant/multiple-graphql-endpoints-with-apollo-client-1a81</link>
      <guid>https://forem.com/codenamegrant/multiple-graphql-endpoints-with-apollo-client-1a81</guid>
      <description>&lt;p&gt;If you are knee-deep in the world of web development, chances are you have crossed paths with GraphQL and Apollo Client. This dynamic duo has revolutionized how developers interact with data on the frontend. However Apollo Client does have a flaw, &lt;em&gt;"How do I connect to multiple GraphQL endpoints using a single client instance?&lt;/em&gt; This post will explain a few ways to achieve just that.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: For the sake of brevity, 'query' will refer to any GraphQL operation; Query, Mutation or Subscription.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ApolloLink.split()
&lt;/h2&gt;

&lt;p&gt;The native approach is to make use of the &lt;code&gt;split&lt;/code&gt; function provided by the &lt;code&gt;@apollo/client&lt;/code&gt;. This will enable your link chain to branch to one of two &lt;code&gt;HttpLink&lt;/code&gt;s based on the result of a boolean check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApolloLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HttpLink&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apollo/client&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;endpoint1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://endpoint1.com/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpoint2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://endpoint2.com/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApolloLink&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;operation&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;apiName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// boolean check&lt;/span&gt;
      &lt;span class="nx"&gt;endpoint1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// if true&lt;/span&gt;
      &lt;span class="nx"&gt;endpoint2&lt;/span&gt;  &lt;span class="c1"&gt;// if fa&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// pass the apiName via the context in your Query&lt;/span&gt;
&lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;QUERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;apiName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the boolean check is based on a context variable, however the test can be anything. The &lt;code&gt;split&lt;/code&gt; function is simple to implement but is limited to only two GQL connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic HttpLink
&lt;/h2&gt;

&lt;p&gt;But now what if a application serves as the frontend for a suite of microservices? Well, by expanding the use of the operation &lt;code&gt;context&lt;/code&gt; the endpoint in the &lt;code&gt;HttpLink&lt;/code&gt; can be set dynamically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createHttpLink&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apollo/client&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;httpLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHttpLink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;getContext&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;apiName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getContext&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;apiName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://endpoint1.com/graphql&lt;/span&gt;&lt;span class="dl"&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;apiName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://endpoint2.com/graphql&lt;/span&gt;&lt;span class="dl"&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;apiName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://endpoint3.com/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;httpLink&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// pass the apiName via the context in your Query&lt;/span&gt;
&lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;QUERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;apiName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;QUERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;apiName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;end2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This solution is also relatively simple and allows for expendability as new microservices are added to the suite that the client app has to connect to. &lt;/p&gt;

&lt;p&gt;The disadvantage of both of these approaches is that the developer has to include the &lt;code&gt;apiName&lt;/code&gt; in the context every time that &lt;code&gt;useQuery&lt;/code&gt;. Personally I found this approach lead to a messy implementation. I mean declaring the context object on &lt;em&gt;every&lt;/em&gt; request is so repetitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apollo MultiAPILink
&lt;/h2&gt;

&lt;p&gt;I wanted a solution that was more intuitive though, something that removed the need to declare the API every time a &lt;code&gt;useQuery&lt;/code&gt; was used, enter &lt;a href="https://github.com/habx/apollo-multi-endpoint-link" rel="noopener noreferrer"&gt;@habx/apollo-multi-endpoint-link&lt;/a&gt;. This library allows the API to be declared on the GQL query as a directive, instead of every time &lt;code&gt;useQuery&lt;/code&gt; is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QUERY1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  query Users @api(name: end1) {
    users { id name }
  }
`&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;QUERY2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  query Companies @api(name: end2) {
    companies { id name code }
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@api(name: end1)&lt;/code&gt; informs the library which endpoint to use based on configuration. &lt;br&gt;
&lt;em&gt;Note: The directive is removed from the query before it is sent to the server.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createHttpLink&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apollo/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MultiAPILink&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@habx/apollo-multi-endpoint-link&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;multiHttpLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MultiAPILink&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;end1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://endpoint1.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;end2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://endpoint2.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;end3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://endpoint3.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt; 
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;createHttpLink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createHttpLink&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;multiHttpLink&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Now use the query without any additional api config&lt;/span&gt;
&lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;QUERY1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;QUERY2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;MultiAPILink&lt;/code&gt; matches the API name with the relevant endpoint. Since the API is declared on the query, it doesn't have to be repeated in every &lt;code&gt;useQuery&lt;/code&gt;, leaving code much cleaner and containing less duplication.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In summary, its definitely possible to have multiple endpoints in a single Apollo Client instance. &lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/habx/apollo-multi-endpoint-link" rel="noopener noreferrer"&gt;Github: @habx/apollo-multi-endpoint-link&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.apollographql.com/docs/react/api/link/introduction/" rel="noopener noreferrer"&gt;Apollo Link Overview&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.apollographql.com/docs/react/api/link/introduction/#directional-composition" rel="noopener noreferrer"&gt;Apollo Link - Directional Composition&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>GraphQL Code Generator with TypeScript, React and Apollo Client</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Fri, 29 Dec 2023 09:55:23 +0000</pubDate>
      <link>https://forem.com/codenamegrant/graphql-code-generator-with-typescript-react-and-apollo-client-4c7b</link>
      <guid>https://forem.com/codenamegrant/graphql-code-generator-with-typescript-react-and-apollo-client-4c7b</guid>
      <description>&lt;p&gt;GraphQL Code Generator is a powerful, open-source, plugin-based tool for streamlining your development by generating boilerplate code from a GQL schema. It can automate the generation of &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typed Queries, Mutations and, Subscriptions for React, Vue, Angular, Next.js &amp;amp; Svelte, whether you are using Apollo Client, URQL or, React Query.&lt;/li&gt;
&lt;li&gt;Typed GraphQL resolvers, for any Node.js (GraphQL Yoga, GraphQL Modules, TypeGraphQL or Apollo) or Java GraphQL server.&lt;/li&gt;
&lt;li&gt;… And more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was my first experience with GraphQL (GQL) and the codegen tool so I'm going to start with what my situation is and how I went about installing and configuring it to serve my use case. I'll include links to additional info as I go.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Setup
&lt;/h2&gt;

&lt;p&gt;The web apps I work on are written in React and TypeScript. We recently migrated some of our servers to use GraphQL instead of REST, so naturally I turned to Apollo Client and started writing react hooks that extended the native &lt;code&gt;useQuery&lt;/code&gt; or &lt;code&gt;useMutation&lt;/code&gt; hooks in Apollo.&lt;br&gt;

  Example
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@apollo/client&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;CHARACTERS_QUERY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  query GetCharacters {
    characters {
      results {
        name
        origin {
          name
        }
      }
    }
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useCharacters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CHARACTERS_QUERY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;This approach became a problem though because obviously the queries were not type safe and so I would need to write out all the TypeScript types, its also a pain to extend! As the application/s grow, I kept manually writing these query/mutation hooks, which became time consuming. There is however a way to use &lt;a href="https://www.apollographql.com/docs/react/development-testing/static-typing/" rel="noopener noreferrer"&gt;TypeScript with Apollo Client&lt;/a&gt; which in turn lead me to the &lt;a href="https://the-guild.dev/graphql/codegen" rel="noopener noreferrer"&gt;GraphQL Code Generator&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating Types
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/CodeNameGrant/gql-codegen/tree/generate-types" rel="noopener noreferrer"&gt;Source Repo&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
The first step I had to take was to generate the TypeScript types from the GQL schema. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install the generator's CLI as well as the TypeScript plugin&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @graphql-codegen/cli @graphql-codegen/typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add a script to your &lt;code&gt;package.json&lt;/code&gt; execute the generator&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;scripts:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"codegen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"graphql-codegen"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create the config file in your root directory named &lt;code&gt;codegen.ts&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CodegenConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@graphql-codegen/cli&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;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CodegenConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;overwrite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rickandmortyapi.com/graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/graphql/generated/graphql.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typescript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This config will inform the generator to use the Rick &amp;amp; Morty API to generate a file (&lt;code&gt;graphql.ts&lt;/code&gt;) using the &lt;a href="https://the-guild.dev/graphql/codegen/plugins/typescript/typescript" rel="noopener noreferrer"&gt;TypeScript plugin&lt;/a&gt;. This will result in the GQL schema being converted into basic TypeScript Types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate React/Apollo Hooks
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/CodeNameGrant/gql-codegen/tree/generate-hooks" rel="noopener noreferrer"&gt;Source Repo&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
The next step will instruct the generator to use predefined queries (or mutations) to generate hooks that execute the queries using Apollo Client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install the &lt;a href="https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-operations" rel="noopener noreferrer"&gt;TypeScript-Operations&lt;/a&gt; and &lt;a href="https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-react-apollo" rel="noopener noreferrer"&gt;TypeScript React Apollo&lt;/a&gt; plugins.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @graphql-codegen/typescript-operations @graphql-codegen/typescript-react-apollo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create &lt;code&gt;src/graphql/documents.graphql&lt;/code&gt; to store the queries to generate hooks for.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Episodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;episodes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;episode&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Characters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;characters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;species&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update the &lt;code&gt;codegen.ts&lt;/code&gt; file to use the above file and plugins to generate the result&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CodegenConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@graphql-codegen/cli&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;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CodegenConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;overwrite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rickandmortyapi.com/graphql&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./**/*.graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;generates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;src/graphql/generated/graphql.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typescript&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;typescript-operations&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;typescript-react-apollo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;withHooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The GQL &lt;a href="https://the-guild.dev/graphql/codegen/docs/config-reference/documents-field" rel="noopener noreferrer"&gt;&lt;code&gt;documents&lt;/code&gt;&lt;/a&gt; can take different patterns. However, after some experimentation, I found that for my use case its easier to just keep them all together in a &lt;code&gt;.graphql&lt;/code&gt; file. The &lt;code&gt;config&lt;/code&gt; attribute is where plugin attributes are specified. The &lt;code&gt;withHooks&lt;/code&gt; attribute is used by &lt;code&gt;typescript-react-apollo&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;npm run codegen&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Once you have run the generator again, you will see that the generated &lt;code&gt;graphql.ts&lt;/code&gt; file now contains hooks that execute the various query variants from Apollo based on the queries we declared, ie &lt;code&gt;useEpisodesQuery&lt;/code&gt;, &lt;code&gt;useEpisodesLazyQuery&lt;/code&gt;, &lt;code&gt;useEpisodesSuspenseQuery&lt;/code&gt;, etc.&lt;/p&gt;
&lt;h3&gt;
  
  
  Generate Hooks near operational files
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/CodeNameGrant/gql-codegen/" rel="noopener noreferrer"&gt;Source Repo&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As I'm sure you noticed from the source code, the app is structured with feature directories but our types and hooks are all generated in single file. However, I want the types to be in their own file to be used anywhere in the app but the documents and generated hooks must be in the feature directories where they are being used.&lt;/p&gt;

&lt;p&gt;To achieve this, I used the &lt;a href="https://the-guild.dev/graphql/codegen/plugins/presets/near-operation-file-preset" rel="noopener noreferrer"&gt;Near Operation File Preset&lt;/a&gt; plugin. This plugin scans a directory for documents and generates the hooks files in the same location.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First install the plugin&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @graphql-codegen/near-operation-file-preset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;In order to use this preset, you need to add 2 outputs to your &lt;code&gt;codegen.ts&lt;/code&gt; file:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first, is the base types, generated by typescript plugin.&lt;/li&gt;
&lt;li&gt;The second, is the one that is in charge of generating types per document.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CodegenConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@graphql-codegen/cli&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;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CodegenConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;overwrite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;generates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/types.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typescript&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./**/*.graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;near-operation-file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;presetConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.generated.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;baseTypesPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;types.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;typescript-operations&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;typescript-react-apollo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;withHooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;npm run codegen&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This will result in a &lt;code&gt;*.generated.ts&lt;/code&gt; file being created for each &lt;code&gt;.graphql&lt;/code&gt; in any directory under &lt;code&gt;src&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The GraphQL Code Generator is in fact a powerful tool that has simplified my development process. It has a large &lt;a href="https://the-guild.dev/graphql/codegen/plugins" rel="noopener noreferrer"&gt;plugin library&lt;/a&gt; that supports many languages and presets and is generally well documented.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>typescript</category>
      <category>react</category>
      <category>apollo</category>
    </item>
    <item>
      <title>Stop Git from tracking file changes</title>
      <dc:creator>CodeNameGrant</dc:creator>
      <pubDate>Fri, 07 Jul 2023 10:56:42 +0000</pubDate>
      <link>https://forem.com/codenamegrant/stop-git-from-tracking-file-changes-1ggg</link>
      <guid>https://forem.com/codenamegrant/stop-git-from-tracking-file-changes-1ggg</guid>
      <description>&lt;p&gt;In this short post I'll explain how and why I like to (sometimes) stop Git from tracking file changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why would I want such a thing?
&lt;/h2&gt;

&lt;p&gt;A simple but real world use case would be when your LOCAL project config differs from the config required in PROD.&lt;/p&gt;

&lt;p&gt;Take a look at this &lt;code&gt;.npmrc&lt;/code&gt; file. Its configured to make use of use an environment variable &lt;code&gt;${NPM_GIT_PAT}&lt;/code&gt; as the &lt;code&gt;authToken&lt;/code&gt; when connecting to the GitHub Package registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@my-private-scope:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${NPM_GIT_PAT}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the file in this state is ready for PROD. When it runs (via GitHub Actions) the &lt;code&gt;NPM_GIT_PAT&lt;/code&gt; environment variable will be declared and populated by a GitHub secret and NPM will be able to install dependencies from a private repo.&lt;/p&gt;

&lt;p&gt;But what about your LOCAL environment? You need to set that &lt;code&gt;authToken&lt;/code&gt; to your own private PAT so you can access those private repos. By telling git to stop tracking changes on the &lt;code&gt;npmrc&lt;/code&gt; file, you can edit it and paste your private token in there without the risk of later accidentally committing it your repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  How
&lt;/h2&gt;

&lt;p&gt;The how is the easy part, git provides a command that let you tell it to stop tracking specific files or folders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git update-index --assume-unchanged path/to/src/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should only be used on files that are already in your repo, if you want git to ignore files from the get-go, add them to you &lt;code&gt;.gitignore&lt;/code&gt; file instead.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Wait, how do I undo it?"&lt;/em&gt;&lt;br&gt;
Its just as easy, replace &lt;code&gt;--assume-unchanged&lt;/code&gt; with &lt;code&gt;--no-assumed-unchanged&lt;/code&gt; and you're set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git update-index --no-assume-unchanged path/to/src/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
      <category>beginners</category>
      <category>development</category>
    </item>
  </channel>
</rss>
