<?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: Mwadali</title>
    <description>The latest articles on DEV Community by Mwadali (@wellingtonmwadali).</description>
    <link>https://dev.to/wellingtonmwadali</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%2F1057478%2F2430d38f-a8ef-4769-9125-c14f27db0897.jpg</url>
      <title>DEV Community: Mwadali</title>
      <link>https://dev.to/wellingtonmwadali</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wellingtonmwadali"/>
    <language>en</language>
    <item>
      <title>Open Source Project Discovery</title>
      <dc:creator>Mwadali</dc:creator>
      <pubDate>Sun, 01 Mar 2026 17:12:17 +0000</pubDate>
      <link>https://dev.to/wellingtonmwadali/open-source-project-discovery-1h9f</link>
      <guid>https://dev.to/wellingtonmwadali/open-source-project-discovery-1h9f</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/weekend-2026-02-28"&gt;DEV Weekend Challenge: Community&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Community
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Open source contributors&lt;/strong&gt; — from nervous first-timers to seasoned maintainers. The people who want to give back, learn by doing, and build something meaningful. But here's the problem: GitHub has 100+ million repositories. Finding the &lt;em&gt;right&lt;/em&gt; project that matches your skills, needs contributors, and has a welcoming community? That's like finding a specific grain of sand on a beach.&lt;/p&gt;

&lt;p&gt;I built this for anyone who's ever:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stared at "good first issue" labels on projects using languages they've never heard of&lt;/li&gt;
&lt;li&gt;Wanted to contribute but had no idea where to start&lt;/li&gt;
&lt;li&gt;Spent hours browsing GitHub, only to find abandoned repos or projects way beyond their skill level&lt;/li&gt;
&lt;li&gt;Felt paralyzed by choice overload&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;🧭 OSS Compass&lt;/strong&gt; — your personal navigator through the open source galaxy.&lt;/p&gt;

&lt;p&gt;You tell it three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Your skill level&lt;/strong&gt; (Beginner / Intermediate / Expert)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Languages you know&lt;/strong&gt; (JavaScript, Python, Rust, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What excites you&lt;/strong&gt; (Web Dev, ML, CLI Tools, Security, Games...)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It hands you 5 active, well-maintained GitHub projects that actually need people like you. Not random repos. Not abandoned projects. Not stuff that's way over your head.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real projects. Real communities. Real impact.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Magic Ingredients
&lt;/h3&gt;

&lt;p&gt;✨ &lt;strong&gt;Smart GitHub Queries&lt;/strong&gt; — Different search strategies for different skill levels. Beginners get projects with 5+ good-first-issues. Experts get high-impact repos with complex challenges.&lt;/p&gt;

&lt;p&gt;🔍 &lt;strong&gt;Live Validation&lt;/strong&gt; — Every project is checked in real-time: Is it active? How many stars? Good first issues? When was the last commit? No dead projects make the cut.&lt;/p&gt;

&lt;p&gt;💾 &lt;strong&gt;Your Journey, Remembered&lt;/strong&gt; — Auto-save your profile, bookmark favorites, track search history. Come back tomorrow, skip the form, see your results.&lt;/p&gt;

&lt;p&gt;⚡ &lt;strong&gt;Supercharged UX&lt;/strong&gt; — Keyboard navigation, animated progress bars, instant filtering by language/stars/activity, mobile-responsive. It &lt;em&gt;feels&lt;/em&gt; good to use.&lt;/p&gt;

&lt;p&gt;🛡️ &lt;strong&gt;Rate Limiting &amp;amp; Caching&lt;/strong&gt; — Smart caching means same profile = instant results. Rate limiting protects the API without killing the experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Live Experience
&lt;/h3&gt;

&lt;p&gt;Try it yourself: &lt;a href="https://open-source-santa-7jnj.vercel.app/" rel="noopener noreferrer"&gt;https://open-source-santa-7jnj.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Tour
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Pick your skill level&lt;br&gt;
![Skill level selection with three cards: Beginner, Intermediate, Expert]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Select your languages (JavaScript, Python, Go, TypeScript...)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Choose your interests (Web Dev, ML, DevTools, Games...)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt; Your personalized project recommendations&lt;br&gt;
![Project cards with stars, languages, descriptions, and action buttons]&lt;/p&gt;

&lt;p&gt;Each project card shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time GitHub stats (⭐ stars, open issues)&lt;/li&gt;
&lt;li&gt;Last commit date (no ghost towns)&lt;/li&gt;
&lt;li&gt;Good-first-issue badge for beginners&lt;/li&gt;
&lt;li&gt;A personalized "why this project" explanation&lt;/li&gt;
&lt;li&gt;Your specific first step to contribute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Filter &amp;amp; Search:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search by name, description, or tags&lt;/li&gt;
&lt;li&gt;Sort by relevance, stars, or recent activity&lt;/li&gt;
&lt;li&gt;Filter by programming language&lt;/li&gt;
&lt;li&gt;Bookmark favorites for later&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/wellingtonmwadali/OpenSourceSanta" rel="noopener noreferrer"&gt;https://github.com/wellingtonmwadali/OpenSourceSanta&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Key Technical Decisions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. No LLM API Needed&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I initially planned to use Claude or GPT for recommendations. Then I realized: GitHub's search API is &lt;em&gt;already&lt;/em&gt; incredibly powerful. Why add cost, latency, and API dependencies when intelligent query construction does the job?&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="c1"&gt;// Different queries for different skill levels&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildSearchQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;interests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;baseCriteria&lt;/span&gt; &lt;span class="o"&gt;=&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;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;beginner&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="nx"&gt;baseCriteria&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;good-first-issues:&amp;gt;3 stars:&amp;gt;100 forks:&amp;gt;10&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="k"&gt;else&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;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;intermediate&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="nx"&gt;baseCriteria&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stars:&amp;gt;500 forks:&amp;gt;50&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;baseCriteria&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stars:&amp;gt;1000 forks:&amp;gt;100&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="c1"&gt;// Combine with languages and interest topics...&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;2. Real-Time Repo Validation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Every project gets validated against 7 criteria: stars, open issues, has good-first-issues labels, last push date, activity status, topics, and homepage. Dead repos never see the light of day.&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;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateAndEnrichRepo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repoSlug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RepoData&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;octokit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;owner&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="c1"&gt;// Check for good-first-issue labels&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;octokit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listLabelsForRepo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;owner&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasGoodFirstIssues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
    &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;good&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; 
    &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;first&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="c1"&gt;// Check if active (pushed within 3 months)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastPush&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pushed_at&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;threeMonthsAgo&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;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;threeMonthsAgo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMonth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;threeMonthsAgo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastPush&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;threeMonthsAgo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stars&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;openIssues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hasGoodFirstIssues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastPushDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isActive&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Client-Side State Machine&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The app has 3 states: &lt;code&gt;form → loading → results&lt;/code&gt;. Each state has its own UX. Loading state cycles through encouraging messages every 2 seconds while showing progress from 0→100%.&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;const&lt;/span&gt; &lt;span class="nx"&gt;LOADING_MESSAGES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Scanning active GitHub repositories…&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="s2"&gt;Checking community health signals…&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="s2"&gt;Matching your skills to open issues…&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="s2"&gt;Evaluating project friendliness…&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="s2"&gt;Curating your perfect matches…&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;&lt;strong&gt;4. Persistent Storage Without a Database&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
localStorage handles everything: user profile, search history (last 5), and bookmarked repos. No backend, no auth, no database. Your browser is your database.&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveProfile&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="nx"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&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="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oss_compass_profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addBookmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repoSlug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;bookmarks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getBookmarks&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;bookmarks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repoSlug&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bookmarks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repoSlug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oss_compass_bookmarks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bookmarks&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Smart Caching with 30-Min TTL&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Same profile within 30 minutes? Instant results from memory. Reduces API calls, speeds up repeat visits, and keeps the GitHub API happy.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 14&lt;/strong&gt; (App Router) — Server components + API routes in one framework&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; — Type safety for GitHub API responses and user data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt; — Rapid UI development with utility classes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Octokit (GitHub API)&lt;/strong&gt; — Official GitHub REST API client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zod&lt;/strong&gt; — Runtime validation for API requests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📦 OSS Compass
├── app/
│   ├── page.tsx                    # Main client component (564 lines of state management)
│   ├── api/recommend/route.ts      # GitHub search + validation logic
│   └── layout.tsx                  # Root layout with metadata
├── components/
│   ├── SkillLevelPicker.tsx        # Visual skill level selector
│   ├── ChipSelector.tsx            # Multi-select chips for languages/interests
│   ├── ProjectCard.tsx             # Rich project display with GitHub data
│   └── StepIndicator.tsx           # Progress visualization
├── lib/
│   ├── github.ts                   # Octokit wrappers + repo validation
│   ├── validation.ts               # Zod schemas for type-safe APIs
│   ├── storage.ts                  # localStorage abstraction
│   ├── cache.ts                    # In-memory request caching
│   ├── rate-limit.ts               # IP-based rate limiting
│   └── constants.ts                # Languages, interests, skill levels
└── types/
    └── index.ts                    # Shared TypeScript types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Build Process
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Day 1: Core MVP&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Built the 3-step form, wired up GitHub API, got basic search working. Realized 60% of results were abandoned repos. Added validation layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 2: UX Polish&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Added keyboard navigation, loading states, progress bars, error handling. Made it feel &lt;em&gt;native&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 3: Features That Matter&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Implemented filtering, sorting, search, bookmarks, and caching. Turned it from a toy into a tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 4: Edge Cases &amp;amp; Performance&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Rate limiting, error messages, mobile responsiveness, accessibility. The unglamorous stuff that makes or breaks real-world use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges &amp;amp; Solutions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; GitHub API rate limits (60 req/hour unauthenticated, 5000/hour with token)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Added caching + rate limiting middleware. Same profile = cached results. Different profiles = track requests per IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Some repos have tons of stars but zero good-first-issues (not beginner-friendly)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Check labels for each repo, surface "good first issue" badge, adjust "why" messaging based on presence of those issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Users don't want to fill out the form every visit&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Auto-save profile to localStorage, show "Welcome back!" with pre-filled form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Results can be overwhelming (language mixing, unclear next steps)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Added language filter, sort options, search bar, and personalized "first step" guidance per project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;Open source contribution is often a "if you know, you know" club. You need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Know which repos are beginner-friendly&lt;/li&gt;
&lt;li&gt;Understand project activity signals (stars, issues, last commit)&lt;/li&gt;
&lt;li&gt;Find projects that match your skills&lt;/li&gt;
&lt;li&gt;Overcome imposter syndrome enough to make that first PR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;OSS Compass removes those barriers.&lt;/strong&gt; It's not just a search tool — it's a matchmaking service for developers and communities.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I Learned
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GitHub's API is underrated.&lt;/strong&gt; With smart queries, you can build powerful discovery tools without LLMs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State management is hard.&lt;/strong&gt; Juggling form state, API state, filter state, and persistent storage taught me to respect React's useEffect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UX microinteractions matter.&lt;/strong&gt; The loading messages, keyboard shortcuts, and animated transitions turned a functional tool into something delightful.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation is never optional.&lt;/strong&gt; Real-world data is messy. Repos can be archived, renamed, or deleted. Always validate before displaying.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What's Next?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub OAuth:&lt;/strong&gt; Personalized recommendations based on your starred repos and contribution history&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weekly Digest Emails:&lt;/strong&gt; "Here are 3 new projects that match your profile"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community Reviews:&lt;/strong&gt; Let users rate/review projects for others&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contribution Tracking:&lt;/strong&gt; Track which projects you've contributed to&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Health Score:&lt;/strong&gt; More nuanced metrics (PR merge rate, issue response time, maintainer activity)&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;The open source community is built on people helping people. But finding where you fit in? That's still too hard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OSS Compass is my contribution to making contribution easier.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've ever wanted to dive into open source but didn't know where to start — this one's for you. 🧭&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with 💙 by &lt;a href="https://github.com/wellingtonmwadali" rel="noopener noreferrer"&gt;Wellington&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Try it live:&lt;/em&gt; &lt;a href="https://open-source-santa-7jnj.vercel.app/" rel="noopener noreferrer"&gt;https://open-source-santa-7jnj.vercel.app/&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Source code:&lt;/em&gt; &lt;a href="https://github.com/wellingtonmwadali/OpenSourceSanta" rel="noopener noreferrer"&gt;https://github.com/wellingtonmwadali/OpenSourceSanta&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
