<?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: Louey Lahwel</title>
    <description>The latest articles on DEV Community by Louey Lahwel (@louey_lahwel_413f2b7772ce).</description>
    <link>https://dev.to/louey_lahwel_413f2b7772ce</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%2F4216023%2F3fbe9f6d-6363-40cc-b90b-866f6f342a11.png</url>
      <title>DEV Community: Louey Lahwel</title>
      <link>https://dev.to/louey_lahwel_413f2b7772ce</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/louey_lahwel_413f2b7772ce"/>
    <language>en</language>
    <item>
      <title>I got tired of my portfolio going stale, so I made one that rebuilds itself from GitHub</title>
      <dc:creator>Louey Lahwel</dc:creator>
      <pubDate>Wed, 04 Nov 2026 17:16:15 +0000</pubDate>
      <link>https://dev.to/louey_lahwel_413f2b7772ce/i-got-tired-of-my-portfolio-going-stale-so-i-made-one-that-rebuilds-itself-from-github-mch</link>
      <guid>https://dev.to/louey_lahwel_413f2b7772ce/i-got-tired-of-my-portfolio-going-stale-so-i-made-one-that-rebuilds-itself-from-github-mch</guid>
      <description>&lt;p&gt;Every developer portfolio I have made was accurate for about a month. After&lt;br&gt;
that it showed what I had built last spring, and the projects I was actually&lt;br&gt;
proud of lived only on GitHub. I'm a data engineer, and data that has to be&lt;br&gt;
copied by hand into a second place goes stale. So I stopped copying it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forkfolio.dev/?ref=devto" rel="noopener noreferrer"&gt;Forkfolio&lt;/a&gt; treats your GitHub account as&lt;br&gt;
the source of truth. You sign in once, and it builds a portfolio from your&lt;br&gt;
public repos, then keeps rebuilding it. Here is how that works and what&lt;br&gt;
broke along the way.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two ways a portfolio finds out you shipped something
&lt;/h2&gt;

&lt;p&gt;There's a nightly job (a Vercel cron at 04:17 UTC) that re-reads every&lt;br&gt;
user's public work: repos, pinned projects, languages, stars and the&lt;br&gt;
contribution calendar. That alone keeps a page at most a day behind.&lt;/p&gt;

&lt;p&gt;Pushes are faster. A GitHub webhook hits an endpoint that checks the HMAC&lt;br&gt;
signature, answers &lt;code&gt;202&lt;/code&gt; straight away and does the sync after the&lt;br&gt;
response. GitHub disables webhooks that answer slowly, so the actual work&lt;br&gt;
can't happen inside the request.&lt;/p&gt;

&lt;p&gt;The part I underestimated was &lt;strong&gt;overlap&lt;/strong&gt;. Seven different things can start a&lt;br&gt;
sync: the cron, two webhooks, the Sync button, signup, installing the GitHub&lt;br&gt;
App and a manual assistant refresh. One &lt;code&gt;git push --tags&lt;/code&gt; alone sends a&lt;br&gt;
delivery per ref. Each sync is a series of delete-then-write steps, so two&lt;br&gt;
running at once duplicated rows and wiped settings people had changed by&lt;br&gt;
hand. Now there's one lock per user. Whoever holds it does the work, anyone&lt;br&gt;
else marks the user "dirty" and leaves, and the holder runs one more pass if&lt;br&gt;
the mark is set when it finishes.&lt;/p&gt;
&lt;h2&gt;
  
  
  One data shape, 21 designs
&lt;/h2&gt;

&lt;p&gt;Every template reads the same object:&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Portfolio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;repos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Repo&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Skill&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;certifications&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Certification&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;experiences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Experience&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;customProjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomProject&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProjectItem&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// repos + custom projects, merged&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because of that, switching design is one click and never loses content. It&lt;br&gt;
also meant the designs could be properly different instead of one layout&lt;br&gt;
recoloured. There are 21 now, each its own scroll-driven page: a 1930s&lt;br&gt;
cartoon with its title cards, a relief atlas, a terminal session you read by&lt;br&gt;
scrolling, a Swiss type specimen. Three are free.&lt;/p&gt;

&lt;h2&gt;
  
  
  An assistant that only knows what you published
&lt;/h2&gt;

&lt;p&gt;Each portfolio has a chat assistant for visitors. A recruiter reading your&lt;br&gt;
page at 3am can ask "what have you built with Python?" and get an answer&lt;br&gt;
from your actual repos. It says that it is an AI, and it answers only from&lt;br&gt;
your public work.&lt;/p&gt;

&lt;p&gt;The most useful fix there was boring. READMEs are cut into chunks before&lt;br&gt;
they're embedded, and only the first chunk of a repo used to carry the&lt;br&gt;
project's name. Every later chunk reached the model as unlabelled text, and&lt;br&gt;
it would happily attribute one project's README to another. Now every chunk&lt;br&gt;
starts with the name of the project it came from, and the prompt treats&lt;br&gt;
project names as facts that must never be renamed or invented.&lt;/p&gt;

&lt;p&gt;For private repos, nothing is read except framework names from the manifest&lt;br&gt;
(&lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;pubspec.yaml&lt;/code&gt; and so on). That way "Flutter" can show up as&lt;br&gt;
a skill without any private code being stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs
&lt;/h2&gt;

&lt;p&gt;The free tier is a hosted portfolio at &lt;code&gt;forkfolio.dev/your-github-login&lt;/code&gt; with&lt;br&gt;
nightly sync, the assistant and three designs. A one-time $19 (a launch&lt;br&gt;
price, normally $29) unlocks all 21 designs and a custom domain. There's no&lt;br&gt;
subscription, because a portfolio isn't something people want to rent.&lt;/p&gt;

&lt;p&gt;About 160 developers have made one since July. Some live examples are on&lt;br&gt;
the &lt;a href="https://forkfolio.dev/showcase" rel="noopener noreferrer"&gt;showcase&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd like to know from you
&lt;/h2&gt;

&lt;p&gt;What would you want a developer portfolio to show that your GitHub profile&lt;br&gt;
can't?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I wrote this with help from an AI assistant; the product, the code&lt;br&gt;
decisions and the numbers are mine.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>portfolio</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
