<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Johannes Posch</title>
    <description>The latest articles on DEV Community by Johannes Posch (@johnnyp).</description>
    <link>https://dev.to/johnnyp</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3132749%2Fd5420499-5e52-4437-89f6-3e557187306f.png</url>
      <title>DEV Community: Johannes Posch</title>
      <link>https://dev.to/johnnyp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnnyp"/>
    <language>en</language>
    <item>
      <title>LanCache Router: Routing and Failover for Technitium</title>
      <dc:creator>Johannes Posch</dc:creator>
      <pubDate>Tue, 20 Oct 2026 20:28:49 +0000</pubDate>
      <link>https://dev.to/johnnyp/lancache-router-routing-and-failover-for-technitium-33ck</link>
      <guid>https://dev.to/johnnyp/lancache-router-routing-and-failover-for-technitium-33ck</guid>
      <description>&lt;p&gt;A local game cache is valuable, but it becomes useless as soon as the DNS sends a bunch of PCs into a cache that is no longer functioning.&lt;/p&gt;

&lt;p&gt;Another problem arises if you have multiple VLANs - the best choice of the cache for one network may turn out to be very bad for the other network. The PC server with a quick access channel to the gaming PCs of your LAN could be placed behind a firewall access point with a significantly lower speed for all the others.&lt;/p&gt;

&lt;p&gt;And I wanted both these problems to work at once – which cache is appropriate for that client? Is the cache online?&lt;/p&gt;

&lt;p&gt;The end product is called &lt;a href="https://github.com/JohannesPosch/Technitium-LanCache-Router" rel="noopener noreferrer"&gt;LanCache Router&lt;/a&gt;, an open source software application developed to work in Technitium DNS Server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I moved the decision into Technitium
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://lancache.net/" rel="noopener noreferrer"&gt;LanCache&lt;/a&gt; stores supported game and update downloads locally, so multiple machines can reuse downloaded content. DNS helps clients find it by returning the cache's address for the relevant download hostnames.&lt;/p&gt;

&lt;p&gt;My previous setup spread the routing and health decisions across several components: dnsdist chose a DNS backend by client subnet, lancache-dns supplied the cache answers, and a separate checker toggled availability. Technitium handled normal DNS resolution.&lt;/p&gt;

&lt;p&gt;That left several services coordinating a decision I wanted to make inside the DNS server. It also tied the setup to one cache and a hard-coded subnet.&lt;/p&gt;

&lt;p&gt;Technitium supports apps that participate in query processing, so I brought the routing, domain matching, and health checks into one app. LanCache still handles the actual downloads and storage.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzf4woe5l02yz353fif14.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzf4woe5l02yz353fif14.png" alt="A gaming client queries Technitium DNS, receives a healthy cache address, and connects directly to that cache for the download." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DNS chooses the destination. The download connection runs directly between the client and the cache.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A route for each client network
&lt;/h2&gt;

&lt;p&gt;The configuration describes cache servers and the networks allowed to use them. A setup could look like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client network&lt;/th&gt;
&lt;th&gt;Preferred cache&lt;/th&gt;
&lt;th&gt;If it is unavailable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gaming PCs&lt;/td&gt;
&lt;td&gt;Cache in the gaming room&lt;/td&gt;
&lt;td&gt;Use the standby cache.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Another building&lt;/td&gt;
&lt;td&gt;Cache in that building&lt;/td&gt;
&lt;td&gt;Use its configured standby.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guests&lt;/td&gt;
&lt;td&gt;No cache route&lt;/td&gt;
&lt;td&gt;Use normal DNS resolution.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a supported query reaching the app, the decision is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find the first enabled route matching the client's source address.&lt;/li&gt;
&lt;li&gt;Select the first Healthy server in that route's preference list.&lt;/li&gt;
&lt;li&gt;Check whether the queried name belongs to the active cache-domain list.&lt;/li&gt;
&lt;li&gt;Return the cache address for an A lookup, or let Technitium continue if those checks do not pass.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A route can also explicitly pass queries through, which is useful for exempting a test PC from its subnet's cache rule.&lt;/p&gt;

&lt;p&gt;Routes use explicit priorities: lower numbers run first, and the first match wins. Put a device exception before the broader subnet route. If the winning route has no healthy server, the app passes through rather than trying a later route.&lt;/p&gt;

&lt;h2&gt;
  
  
  One subnet, two caches
&lt;/h2&gt;

&lt;p&gt;Here is a complete configuration for a preferred cache and a standby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cacheServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cache-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.3.44"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cache-2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.4.44"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sourceCidr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.3.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"cache-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cache-2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the addresses and subnet with your own. Both caches must be reachable by the routed clients and by the DNS server.&lt;/p&gt;

&lt;p&gt;For matching download domains, clients in &lt;code&gt;192.168.3.0/24&lt;/code&gt; receive &lt;code&gt;cache-1&lt;/code&gt; while it is Healthy. If it becomes unavailable, new answers use &lt;code&gt;cache-2&lt;/code&gt;, provided that server is Healthy. If neither qualifies, Technitium resolves the lookup normally under its existing rules.&lt;/p&gt;

&lt;p&gt;Clients outside the subnet have no matching route, so their lookups follow normal DNS processing. You could add a second route for &lt;code&gt;192.168.4.0/24&lt;/code&gt; and reverse the preference list, giving each network its nearby cache first.&lt;/p&gt;

&lt;p&gt;This is ordered failover. The app selects one cache at a time for each route; it does not balance downloads across all healthy servers.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs1dcnej613jl1ks8gxqu.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs1dcnej613jl1ks8gxqu.png" alt="Three health snapshots: cache-1 is selected while healthy, cache-2 is selected when only the standby is healthy, and normal DNS resolution continues when neither is healthy." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The route's preference order decides each new answer. Clients may retain an earlier answer until its TTL expires.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Health checks without probing on every lookup
&lt;/h2&gt;

&lt;p&gt;The default check requests &lt;code&gt;/lancache-heartbeat&lt;/code&gt; over HTTP on port 80 and accepts a &lt;code&gt;200&lt;/code&gt; or &lt;code&gt;204&lt;/code&gt; response. TCP and HTTPS checks are also available, with shared defaults and per-server overrides.&lt;/p&gt;

&lt;p&gt;Probes run in the background. Query handling reads the latest health state and domain matcher without making a network request of its own.&lt;/p&gt;

&lt;p&gt;By default, two consecutive failures mark a cache Unhealthy, and three consecutive successes make it Healthy. Newly started caches begin Unknown and use faster startup checks until their state settles. A preferred cache returns to selection automatically after it recovers.&lt;/p&gt;

&lt;p&gt;There is a timing limit here that matters in practice: &lt;strong&gt;changing a DNS answer does not move an existing download.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app first needs to detect the failure. Clients may then retain an earlier answer until its TTL expires; the default is 60 seconds. Open connections continue independently. Failover improves subsequent lookups, but it cannot make an interrupted connection switch servers instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the domain list current
&lt;/h2&gt;

&lt;p&gt;The app downloads the community &lt;a href="https://github.com/uklans/cache-domains" rel="noopener noreferrer"&gt;uklans/cache-domains&lt;/a&gt; list, with a configurable refresh schedule. You can include or exclude services, add download hosts, and carve individual names out of broader wildcard entries.&lt;/p&gt;

&lt;p&gt;A failed refresh keeps the last successful list active. Each DNS node also saves a snapshot for ordinary restarts, so a temporary source outage does not require discarding a list it already has. A first installation still needs a successful download before cache-domain matching becomes available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before trying it on your network
&lt;/h2&gt;

&lt;p&gt;There are a few boundaries to account for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Technitium needs to see the client address.&lt;/strong&gt; If NAT or a forwarding resolver makes every query appear to come from one gateway, subnet routes cannot distinguish the original clients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health checks run from the DNS node.&lt;/strong&gt; A successful probe cannot detect a firewall rule that blocks only the clients. In a cluster, each node probes independently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache addresses are currently IPv4 only.&lt;/strong&gt; Eligible AAAA, HTTPS, and SVCB queries receive empty successful answers by default, keeping IPv6 addresses and service hints from bypassing the cache. Those modes are configurable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing DNS rules still matter.&lt;/strong&gt; Local zones and other apps can take precedence. Passthrough follows Technitium's normal behavior, including its blocking and upstream settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is also no per-service routing yet: a client cannot send Steam downloads to one cache and Epic downloads to another through separate service rules. IPv6 cache addresses, per-service routing, and a diagnostic TXT query are recorded in the backlog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;You need a working LanCache and a compatible Technitium installation. Install the app ZIP through Technitium's &lt;strong&gt;Apps&lt;/strong&gt; page, open its configuration, and start with one client subnet. The packaged configuration is disabled until you supply your settings and enable it.&lt;/p&gt;

&lt;p&gt;Once the cache is Healthy and the domain list has loaded, test from a client in that subnet. Replace the DNS address below with yours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig @192.168.3.53 lancache.steamcontent.com A +short
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the default list and the example configuration, the answer should be &lt;code&gt;192.168.3.44&lt;/code&gt; while the preferred cache is Healthy. The &lt;a href="https://github.com/JohannesPosch/Technitium-LanCache-Router/blob/HEAD/docs/wiki/03-installation.md" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt; covers package compatibility, building from source, and checking fallback.&lt;/p&gt;

&lt;p&gt;The project is &lt;a href="https://github.com/JohannesPosch/Technitium-LanCache-Router/blob/HEAD/LICENSE" rel="noopener noreferrer"&gt;MIT licensed&lt;/a&gt;, with a &lt;a href="https://github.com/JohannesPosch/Technitium-LanCache-Router/blob/HEAD/docs/wiki/README.md" rel="noopener noreferrer"&gt;wiki&lt;/a&gt; covering configuration, routing examples, and troubleshooting.&lt;/p&gt;

&lt;p&gt;If this fits your setup, &lt;a href="https://github.com/JohannesPosch/Technitium-LanCache-Router" rel="noopener noreferrer"&gt;take a look at the repository&lt;/a&gt;. Feedback from people running several VLANs or multiple DNS nodes would be especially useful. An issue with your intended topology, expected behavior, and relevant logs gives me something concrete to work with.&lt;/p&gt;

&lt;p&gt;How are you handling cache availability across your networks today?&lt;/p&gt;

</description>
      <category>networking</category>
      <category>devops</category>
      <category>dns</category>
      <category>lancache</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Johannes Posch</dc:creator>
      <pubDate>Mon, 26 May 2025 09:42:25 +0000</pubDate>
      <link>https://dev.to/johnnyp/-3ghb</link>
      <guid>https://dev.to/johnnyp/-3ghb</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/johnnyp/code-template-hub-how-i-supercharged-my-development-workflow-and-you-can-too-3ni4" class="crayons-story__hidden-navigation-link"&gt;Code Template Hub: How I Supercharged My Development Workflow (And You Can Too!)&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/johnnyp" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3132749%2Fd5420499-5e52-4437-89f6-3e557187306f.png" alt="johnnyp profile" class="crayons-avatar__image" width="128" height="128"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/johnnyp" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Johannes Posch
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Johannes Posch
                
                
              
              &lt;div id="story-author-preview-content-2465326" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/johnnyp" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3132749%2Fd5420499-5e52-4437-89f6-3e557187306f.png" class="crayons-avatar__image" alt="" width="128" height="128"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Johannes Posch&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/johnnyp/code-template-hub-how-i-supercharged-my-development-workflow-and-you-can-too-3ni4" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 7 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/johnnyp/code-template-hub-how-i-supercharged-my-development-workflow-and-you-can-too-3ni4" id="article-link-2465326"&gt;
          Code Template Hub: How I Supercharged My Development Workflow (And You Can Too!)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/vscode"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;vscode&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/productivity"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;productivity&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/git"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;git&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tooling"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tooling&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/johnnyp/code-template-hub-how-i-supercharged-my-development-workflow-and-you-can-too-3ni4#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>git</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Code Template Hub: How I Supercharged My Development Workflow (And You Can Too!)</title>
      <dc:creator>Johannes Posch</dc:creator>
      <pubDate>Wed, 07 May 2025 14:04:32 +0000</pubDate>
      <link>https://dev.to/johnnyp/code-template-hub-how-i-supercharged-my-development-workflow-and-you-can-too-3ni4</link>
      <guid>https://dev.to/johnnyp/code-template-hub-how-i-supercharged-my-development-workflow-and-you-can-too-3ni4</guid>
      <description>&lt;p&gt;Hey there! Ever found yourself copying and pasting the same component structure for the tenth time this week? Or maybe you've spent way too much time fixing inconsistencies in boilerplate code when you could be building actual features? Trust me, I've been there too, and it was driving me crazy.&lt;/p&gt;

&lt;p&gt;That frustration led me to create Code Template Hub, a VS Code extension that's completely changed how I approach repetitive coding tasks. I'm excited to share it with you today!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built This (My Story)
&lt;/h2&gt;

&lt;p&gt;Let me tell you a bit about my situation. I split my coding time between my day job at a company and my projects at home. These two worlds have totally different requirements, coding standards, and project structures.&lt;/p&gt;

&lt;p&gt;At work, we needed standardized templates that every team member could use to ensure consistency across our codebase. Nothing kills productivity like having five developers each implementing components in their own unique way!&lt;/p&gt;

&lt;p&gt;For my personal projects, I wanted something more flexible that matched my personal coding style. I didn't want to compromise on either front - I needed templates that would work seamlessly in both contexts.&lt;/p&gt;

&lt;p&gt;I searched and searched for an extension that could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Be flexible enough for ANY templating situation&lt;/li&gt;
&lt;li&gt;Support different template sets for work vs. personal projects&lt;/li&gt;
&lt;li&gt;Play nicely with version control (crucial for team collaboration!)&lt;/li&gt;
&lt;li&gt;Be simple enough that my colleagues would actually use it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nothing quite fit the bill. So I did what developers do best - I built it myself! Code Template Hub is the result of scratching my own itch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Code Template Hub Does For Me (And Can Do For You)
&lt;/h2&gt;

&lt;p&gt;In simple terms, Code Template Hub lets me quickly generate files from templates stored in git repositories. But it's so much more than that.&lt;/p&gt;

&lt;p&gt;Think of it as your personal coding assistant that knows exactly how you (or your team) like to structure your files. Need a new React component with all the trimmings? Two clicks. API service with proper error handling? Three clicks. Documentation file with your team's exact required sections? Done.&lt;/p&gt;

&lt;p&gt;The best part? The templates evolve as my needs change, and I can have completely different templates for work versus personal projects. No more context switching headaches!&lt;/p&gt;

&lt;h2&gt;
  
  
  My Favorite Features (That You'll Love Too)
&lt;/h2&gt;

&lt;p&gt;Here's what makes Code Template Hub so powerful in my daily workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  📦 Repository-based Templates with Automatic Updates
&lt;/h3&gt;

&lt;p&gt;All my templates live in git repositories - just like my code. This means they're versioned, trackable, and shareable with my team. I configure my repositories through VS Code settings, which lets everyone on the team access the same templates.&lt;/p&gt;

&lt;p&gt;The best part? Templates update automatically when VS Code starts. This means my team is always using the latest version - no more "oh, I was using the old template" problems!&lt;/p&gt;

&lt;h3&gt;
  
  
  💪 Powerful Templating Engine That Does The Heavy Lifting
&lt;/h3&gt;

&lt;p&gt;I'm using dot.js as the templating engine, which gives me incredible flexibility. I can add loops, conditionals, and transformations directly in my templates. I've created templates that adapt to different component types, styling approaches, and project structures - all from a single template file.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧩 Smart Parameters That Adjust To What You Need
&lt;/h3&gt;

&lt;p&gt;One of my favorite innovations is how parameters work. I can define what information I need from the user, add validation to prevent mistakes, and even make certain prompts appear or disappear based on previous answers.&lt;/p&gt;

&lt;p&gt;For example, if a user selects "Include Tests" for a component, only then do I show them options for test configuration. This keeps things clean and focused.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔮 Advanced Variables For Intelligent Templates
&lt;/h3&gt;

&lt;p&gt;The variables feature has been a complete game-changer for me. Beyond just collecting user input, I can define sophisticated variables that preprocess parameters or analyze the file structure where the template will be created.&lt;/p&gt;

&lt;p&gt;What makes this truly powerful is the ability to define custom JavaScript functions (in separate files) on a template-by-template basis. These functions can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch information from web APIs&lt;/li&gt;
&lt;li&gt;Analyze the project structure&lt;/li&gt;
&lt;li&gt;Transform user inputs into exactly what the template needs&lt;/li&gt;
&lt;li&gt;Generate complex derived values based on multiple parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For instance, in one of my templates, I created a function that checks our API documentation and automatically pulls in the latest endpoint structure. Another template examines the surrounding folder structure to adapt its output to match existing patterns.&lt;/p&gt;

&lt;p&gt;Since these are full JavaScript functions with access to the workspace context, the possibilities are virtually limitless. It's like having a mini-AI assistant that understands your project before generating files!&lt;/p&gt;

&lt;h3&gt;
  
  
  👥 Author Info That's Always Correct
&lt;/h3&gt;

&lt;p&gt;I've set up author and organization information in my VS Code settings, which means every file created has consistent attribution. My team members do the same, so we always know who created what. It's a small thing, but it adds accountability and helps with code ownership questions down the road.&lt;/p&gt;

&lt;h3&gt;
  
  
  🗂️ Complex File Structures With One Click
&lt;/h3&gt;

&lt;p&gt;My absolute favorite capability is creating multiple files from a single template. For example, when I create a new feature, I need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component files&lt;/li&gt;
&lt;li&gt;Style files&lt;/li&gt;
&lt;li&gt;Test files&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Index exports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before, this was a tedious, error-prone process. Now, I select one template and get a complete, perfectly structured feature folder. It's almost magical how much time this saves!&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Use It (And How You Can Too)
&lt;/h2&gt;

&lt;p&gt;Here's my typical workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I've set up two repositories: one for work templates and one for personal projects&lt;/li&gt;
&lt;li&gt;I create template files using dot.js syntax, adding placeholders for things like component names&lt;/li&gt;
&lt;li&gt;When I need a new feature, I right-click in VS Code, select "New from Template...," and follow the prompts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The extension handles the rest, processing each template file and replacing placeholders with my input. The first time I showed this to a colleague, their jaw literally dropped. "You made that whole folder structure with two clicks?!" was the reaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Examples That Transformed My Workflow
&lt;/h2&gt;

&lt;p&gt;Here's how Code Template Hub has changed my daily coding life:&lt;/p&gt;

&lt;h3&gt;
  
  
  React Components That Just Make Sense
&lt;/h3&gt;

&lt;p&gt;Creating React components used to be tedious. Now, one template generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A properly structured component file&lt;/li&gt;
&lt;li&gt;Styling that matches our project's approach (CSS modules, styled-components, or Tailwind)&lt;/li&gt;
&lt;li&gt;Index exports for clean imports&lt;/li&gt;
&lt;li&gt;Test files with proper testing library setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The template even adapts based on the parameters I choose. If I select "Include Hooks," it automatically adds useState and useEffect imports and boilerplate. If I choose "With TypeScript," it adds the proper type definitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Integration Without The Boilerplate
&lt;/h3&gt;

&lt;p&gt;API integration always required tons of repetitive code. With Code Template Hub, I created a template that generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service functions with proper error handling&lt;/li&gt;
&lt;li&gt;Type definitions that match our API structure&lt;/li&gt;
&lt;li&gt;Mock data for testing scenarios&lt;/li&gt;
&lt;li&gt;Documentation comments with usage examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conditional prompts feature really shines here - I only see options relevant to the type of API I'm integrating with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation That People Actually Write
&lt;/h3&gt;

&lt;p&gt;Let's be honest - documentation is often neglected because it's tedious to set up properly. My documentation template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates files with all required sections pre-populated&lt;/li&gt;
&lt;li&gt;Automatically includes author information&lt;/li&gt;
&lt;li&gt;Sets up the proper formatting and structure&lt;/li&gt;
&lt;li&gt;Adds the creation date and basic metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since implementing this, our team's documentation quality has dramatically improved!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started (It's Easier Than You Think!)
&lt;/h2&gt;

&lt;p&gt;Ready to try it yourself? Here's how to get started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Code Template Hub from the VS Code Marketplace&lt;/li&gt;
&lt;li&gt;Configure at least one template repository in your VS Code settings&lt;/li&gt;
&lt;li&gt;Run "Code Template Hub: Refresh Templates" to fetch your templates&lt;/li&gt;
&lt;li&gt;Right-click in Explorer and select "New from Template..."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it! Your first template creation is just a few clicks away.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Templates Are Structured (For The Curious)
&lt;/h2&gt;

&lt;p&gt;If you're interested in creating your own templates, here's a peek at how they're structured:&lt;/p&gt;

&lt;p&gt;A template repository must contain a &lt;code&gt;templates.json&lt;/code&gt; file in the root directory that defines all available templates and their parameters. This file is essential as it's what the extension looks for when loading templates. The template files themselves live in a subdirectory.&lt;/p&gt;

&lt;p&gt;For example, a React component template might include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/
├── templates.json
├── templates/
│   ├── react/
│   │   ├── component.dot.js
│   │   ├── styles.dot.js
│   │   └── index.dot.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beauty of this system is that you can structure your templates however makes sense for your workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Frustration To Freedom
&lt;/h2&gt;

&lt;p&gt;Before I created Code Template Hub, I was spending hours each week on repetitive file creation tasks. Now, that time is spent actually solving interesting problems and building features.&lt;/p&gt;

&lt;p&gt;The ability to use different template sets for my company work versus personal projects has been a complete game-changer. I no longer have to context-switch between different coding standards - my templates handle that mental overhead for me.&lt;/p&gt;

&lt;p&gt;If you're like me and tired of repetitive boilerplate tasks stealing your coding joy, give Code Template Hub a try. I built it to scratch my own itch, but I'd be thrilled if it helps you too!&lt;/p&gt;




&lt;p&gt;Check out &lt;a href="https://github.com/JohannesPosch/Code-Template-Hub" rel="noopener noreferrer"&gt;Code Template Hub on GitHub&lt;/a&gt; and install it from the &lt;a href="https://marketplace.visualstudio.com/items?itemName=JohnnyP.codeTemplateHub" rel="noopener noreferrer"&gt;VS Code Marketplace&lt;/a&gt; today. I'd love to hear how you're using it and any suggestions for making it even better!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>productivity</category>
      <category>git</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
