<?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: Karam Khoury</title>
    <description>The latest articles on DEV Community by Karam Khoury (@karamkhoury88).</description>
    <link>https://dev.to/karamkhoury88</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%2F4045189%2Fe44cb307-6c08-4fc0-8ff8-6bf3fa1dbfcc.png</url>
      <title>DEV Community: Karam Khoury</title>
      <link>https://dev.to/karamkhoury88</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/karamkhoury88"/>
    <language>en</language>
    <item>
      <title>Oops, I Committed a Secret: A Calm Guide to Scrubbing Git History</title>
      <dc:creator>Karam Khoury</dc:creator>
      <pubDate>Fri, 24 Jul 2026 12:00:25 +0000</pubDate>
      <link>https://dev.to/karamkhoury88/oops-i-committed-a-secret-a-calm-guide-to-scrubbing-git-history-38ll</link>
      <guid>https://dev.to/karamkhoury88/oops-i-committed-a-secret-a-calm-guide-to-scrubbing-git-history-38ll</guid>
      <description>&lt;p&gt;You just finished a great feature. You're in the flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Fixed the bug!"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you see it, staring back from the GitHub UI: your live API key. 💀&lt;/p&gt;

&lt;p&gt;We've all been there. Here's the calm, correct order of operations — from "quick save" for a mistake you caught instantly, to the deep clean for a secret that's been buried in history for months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: Rotate the secret first (yes, before anything else)
&lt;/h2&gt;

&lt;p&gt;This is the step people skip, and it's the only one that's non-negotiable. &lt;strong&gt;The moment a secret hits a remote, assume it's compromised.&lt;/strong&gt; Bots scrape public GitHub for credentials within &lt;em&gt;seconds&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So before you touch history:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Revoke / rotate&lt;/strong&gt; the leaked key, token, or password at its source (AWS, Stripe, your DB, wherever).&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Then&lt;/em&gt; clean the repo.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cleaning history without rotating is like changing the locks after posting the old key online. Rotate first, always.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 1: You caught it before pushing (the quick save)
&lt;/h2&gt;

&lt;p&gt;If the secret is only in your &lt;strong&gt;last, un-pushed commit&lt;/strong&gt;, this is trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stop tracking the file and ignore it going forward&lt;/span&gt;
git &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;--cached&lt;/span&gt; .env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore

&lt;span class="c"&gt;# Fold the fix into the last commit&lt;/span&gt;
git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &lt;span class="nt"&gt;--no-edit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. Nothing left the building. (Still rotate the key if it was ever real.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Case 2: It's deep in history, or already pushed (the deep clean)
&lt;/h2&gt;

&lt;p&gt;Once the secret is several commits back — or on the remote — you need to rewrite history. The modern, recommended tool is &lt;strong&gt;&lt;code&gt;git filter-repo&lt;/code&gt;&lt;/strong&gt; (Git's own docs now steer you here instead of the old &lt;code&gt;filter-branch&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install (Python)&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;git-filter-repo

&lt;span class="c"&gt;# Option A: remove a whole file from ALL history&lt;/span&gt;
git filter-repo &lt;span class="nt"&gt;--path&lt;/span&gt; config/secrets.yml &lt;span class="nt"&gt;--invert-paths&lt;/span&gt;

&lt;span class="c"&gt;# Option B: redact a specific string everywhere it appears&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'AKIAIOSFODNN7EXAMPLE==&amp;gt;REDACTED'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; replacements.txt
git filter-repo &lt;span class="nt"&gt;--replace-text&lt;/span&gt; replacements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefer &lt;strong&gt;BFG Repo-Cleaner&lt;/strong&gt; if you like a faster, simpler tool for the common cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bfg &lt;span class="nt"&gt;--delete-files&lt;/span&gt; id_rsa
bfg &lt;span class="nt"&gt;--replace-text&lt;/span&gt; passwords.txt

&lt;span class="c"&gt;# Expire the old refs and garbage-collect&lt;/span&gt;
git reflog expire &lt;span class="nt"&gt;--expire&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git gc &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--aggressive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Then: force-push and warn your team
&lt;/h2&gt;

&lt;p&gt;History rewriting changes commit hashes, so you must overwrite the remote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ This rewrites shared history. Tell every collaborator to &lt;strong&gt;re-clone or hard-reset&lt;/strong&gt; — if someone merges an old local branch, the secret comes right back. And remember: on GitHub, the old commit can still be reachable via its hash / cached views for a while, which is exactly why &lt;strong&gt;Step 0 (rotation)&lt;/strong&gt; is what actually protects you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prevention: never live on the edge again
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.gitignore&lt;/code&gt; first.&lt;/strong&gt; Add &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;*.pem&lt;/code&gt;, &lt;code&gt;secrets.*&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; the first commit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment variables / a secret manager&lt;/strong&gt; (Azure Key Vault, AWS Secrets Manager, Doppler) — secrets never touch the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A pre-commit scanner&lt;/strong&gt; so a secret is blocked &lt;em&gt;before&lt;/em&gt; it's committed:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# gitleaks catches secrets in staged changes&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;gitleaks
gitleaks protect &lt;span class="nt"&gt;--staged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire that into a pre-commit hook and the "cold sweat" moment mostly disappears.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rotate the secret&lt;/strong&gt; (non-negotiable, do it first).&lt;/li&gt;
&lt;li&gt;Un-pushed? &lt;code&gt;git commit --amend&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In history? &lt;code&gt;git filter-repo&lt;/code&gt; or BFG, then &lt;code&gt;git push --force&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Tell your team to re-clone.&lt;/li&gt;
&lt;li&gt;Add a pre-commit scanner so it never happens again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How do you handle secrets in your workflow — pre-commit hooks, or living life on the edge? 👇&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by Karam Khoury — Lead Software Engineer (.NET &amp;amp; Azure) with 14+ years building secure, scalable fintech systems. More at &lt;a href="https://karamkhoury.me" rel="noopener noreferrer"&gt;karamkhoury.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>security</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>System Design: The 6 Core Load Balancing Algorithms, Explained</title>
      <dc:creator>Karam Khoury</dc:creator>
      <pubDate>Fri, 24 Jul 2026 11:58:19 +0000</pubDate>
      <link>https://dev.to/karamkhoury88/system-design-the-6-core-load-balancing-algorithms-explained-2gc</link>
      <guid>https://dev.to/karamkhoury88/system-design-the-6-core-load-balancing-algorithms-explained-2gc</guid>
      <description>&lt;p&gt;Stop guessing how your traffic scales — understand the routing mechanics. Here's how the six core algorithms actually dictate traffic distribution, and when to reach for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Round Robin
&lt;/h2&gt;

&lt;p&gt;Distributes requests sequentially across the server pool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Dealing a deck of cards — Server A gets request 1, Server B gets 2, Server C gets 3, then the cycle repeats. Best for identical, stateless servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Weighted Round Robin
&lt;/h2&gt;

&lt;p&gt;Sequential routing that accounts for unequal hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Server A has 64 GB RAM, Server B has 32 GB. Assign weight 2 to A and 1 to B, and A receives exactly twice as many requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Least Connections
&lt;/h2&gt;

&lt;p&gt;Routes traffic to the server with the fewest active, open sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Joining the supermarket checkout with the fewest people. Ideal for long-lived connections — WebSockets, heavy database queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. IP Hash
&lt;/h2&gt;

&lt;p&gt;Uses a hash of the client's IP to guarantee the same client hits the same server every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A user at 192.168.1.50 is always routed to Server C. Mandatory for legacy stateful apps that store session data in-memory rather than a distributed cache.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Least Response Time
&lt;/h2&gt;

&lt;p&gt;A hybrid: lowest number of active connections combined with the lowest average response time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Joining the line that has both the fewest people &lt;em&gt;and&lt;/em&gt; the fastest cashier. Essential when server performance degrades unpredictably.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Adaptive (Resource-Based)
&lt;/h2&gt;

&lt;p&gt;Routes based on real-time hardware telemetry from an agent on each server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; The load balancer queries CPU, memory, and I/O wait in real time, and avoids any server spiking above 80% utilization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Choose your algorithm based on your infrastructure and state management — not trends. Build systems that scale predictably.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written by Karam Khoury — Lead Software Engineer (.NET &amp;amp; Azure), building secure, scalable fintech systems. More at &lt;a href="https://karamkhoury.me" rel="noopener noreferrer"&gt;karamkhoury.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
