<?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: heather</title>
    <description>The latest articles on DEV Community by heather (@heatherp).</description>
    <link>https://dev.to/heatherp</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%2F3249982%2F1abac070-ca9f-47cc-bc18-331a9bf26d74.jpg</url>
      <title>DEV Community: heather</title>
      <link>https://dev.to/heatherp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/heatherp"/>
    <language>en</language>
    <item>
      <title>Grokking with ChatGPT - The "Triple Sum Close to Target" algorithm, explained.</title>
      <dc:creator>heather</dc:creator>
      <pubDate>Fri, 15 Aug 2025 18:52:58 +0000</pubDate>
      <link>https://dev.to/heatherp/grokking-with-chatgpt-the-triple-sum-close-to-target-algorithm-explained-h5a</link>
      <guid>https://dev.to/heatherp/grokking-with-chatgpt-the-triple-sum-close-to-target-algorithm-explained-h5a</guid>
      <description>&lt;p&gt;This morning I was going crazy trying to complete "Triplet Sum Close to Target", a medium-level two-pointer algorithm. Basically, &lt;/p&gt;

&lt;p&gt;I could not "grok" how a number's difference could be used to evaluate its size relative to another number. I was also trying to keep track of too many numbers in my head. Here's a convo with ChatGPT that helped a lot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World example of differences - Shot Put
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;me&lt;/strong&gt;: Can you help me understand why using the diff is key to determining if two numbers are closer than two other numbers?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gpt&lt;/strong&gt;: Yep — this is the heart of why “the diff” is the comparison tool in these problems.&lt;/p&gt;

&lt;p&gt;Let’s slow down and strip away the “triplet” part — we’ll just compare two sums and see which is closer to the target.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is diff?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We define:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff = target - sum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If diff is positive, the sum is too small (we’re under target by diff units).&lt;/li&gt;
&lt;li&gt;If diff is negative, the sum is too big (we’re over target by |diff| units).&lt;/li&gt;
&lt;li&gt;If diff is zero, the sum exactly matches the target.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Why not compare sums directly?&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;If you compare sums directly, you have to handle two cases:&lt;/li&gt;
&lt;li&gt;Sum is less than target (how much less?)&lt;/li&gt;
&lt;li&gt;Sum is greater than target (how much more?)&lt;/li&gt;
&lt;li&gt;The diff unifies these into one number that encodes both direction (sign) and distance (magnitude).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the key insight is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff = target - sum shifts the problem so that “closeness to target” becomes “closeness to zero,” which is way easier to reason about and compare.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;me:&lt;/strong&gt; ok, I see. I'm picturing a shot put competition and how far someone is throwing something. That helps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gpt&lt;/strong&gt;:&lt;br&gt;
Got it — let’s put your triple sum closest loop into shot put competition mode so every diff update feels like a throw getting measured.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vocabulary swap&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;target → the mark on the field where you want to land.&lt;br&gt;
sum → the throw’s landing spot.&lt;br&gt;
diff = target - sum → how far you missed the mark and in which direction:&lt;br&gt;
Positive diff → short throw (didn’t reach the mark).&lt;br&gt;
Negative diff → long throw (overshot the mark).&lt;br&gt;
Zero diff → bullseye.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Algorithm in “coach-speak”
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int mark = targetSum;  // the distance you want to throw
Integer bestMiss = null; // best miss distance so far (diff)

for (int p = 0; p &amp;lt; arr.length - 2; p++) {
    int l = p + 1;
    int r = arr.length - 1;

    while (l &amp;lt; r) {
        int throwSpot = arr[p] + arr[l] + arr[r];
        int miss = mark - throwSpot; // how far you missed the mark

        if (miss == 0) {
            // Landed exactly on the mark, competition over
            return throwSpot;
        }

        if (bestMiss == null || isCloserToMark(miss, bestMiss)) {
            // New best throw
            bestMiss = miss;
        }

        // Adjust next throw attempt:
        if (throwSpot &amp;lt; mark) {
            // Short throw — try to throw further
            l++;
        } else {
            // Long throw — pull back a little
            r--;
        }
    }
}

private boolean isCloserToMark(int newMiss, int bestMiss) {
    // Case 1: both throws land short
    if (newMiss &amp;gt;= 0 &amp;amp;&amp;amp; bestMiss &amp;gt;= 0) {
        return newMiss &amp;lt; bestMiss; // smaller miss is closer
    }
    // Case 2: both throws land long
    if (newMiss &amp;lt; 0 &amp;amp;&amp;amp; bestMiss &amp;lt; 0) {
        return newMiss &amp;gt; bestMiss; // less negative miss is closer
    }
    // Case 3: one short, one long — choose the one closer to zero
    return Math.abs(newMiss) &amp;lt; Math.abs(bestMiss); // &amp;lt;-- here you could replace abs with sign logic if you want to go full no-abs
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;me&lt;/strong&gt;: I get it. The one with the bigger sign in the end "wins". It was far enough away from zero to preserve its sign! Thus the other one was closer to zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gpt&lt;/strong&gt;: Exactly — you nailed it.&lt;/p&gt;

&lt;p&gt;When the signs differ, the one with the bigger magnitude will “pull” the total (diff + bestDiff) over to its side of zero.&lt;br&gt;
So if adding them doesn’t pull the sum away from bestDiff’s sign, it means the new one (diff) must have had the smaller magnitude — it was closer to zero.&lt;/p&gt;

&lt;p&gt;That’s the little mental judo move that lets you compare distances without ever taking an absolute value.&lt;/p&gt;

&lt;p&gt;It’s like two kids in a tug-of-war:&lt;/p&gt;

&lt;p&gt;If the rope ends up on the same side it started, the other kid wasn’t strong enough to pull it across — meaning they were “weaker” (smaller magnitude).&lt;/p&gt;

&lt;p&gt;If the rope flips sides, the new kid overpowered the old one.&lt;/p&gt;

&lt;p&gt;Want me to diagram that tug-of-war analogy with number lines so you can see it visually?&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Simplify when Thinking about Algorithms
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;me&lt;/strong&gt;: I was trying to relate the diffs to the sums instead of just thinking about how the diffs relate and that was too many variables to keep in my head.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gpt&lt;/strong&gt;: &lt;/p&gt;

&lt;p&gt;Yep — that’s a super common trap.&lt;/p&gt;

&lt;p&gt;When you keep flipping back and forth between sum space (“how far is this sum from target?”) and diff space (“how far is this number from zero?”), your brain ends up juggling two number lines at the same time.&lt;br&gt;
That’s cognitive overload — especially when the signs flip.&lt;/p&gt;

&lt;p&gt;The beauty of using diff consistently is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You stay in one number line (zero is the goalpost).&lt;/li&gt;
&lt;li&gt;Every comparison is about one thing: “Which is closer to zero?”&lt;/li&gt;
&lt;li&gt;You don’t have to re-map to the original sums until the very end, once you know the winning diff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s like translating everything into one currency before comparing prices — instead of trying to mentally compare yen to dollars to euros all at once.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Then, because we all know chatGPT likes to offer things :-) it made me this checklist to avoid overthinking in algorithm problems. I thought I'd share&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;gpt&lt;/strong&gt;: here’s a pattern list you can keep in mind for avoiding overload in algorithm problems.&lt;br&gt;
Think of it as your “mental clutter cleanup” checklist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Distance / Target Problems → Switch to Diff Space&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Closest sum, nearest point, minimal adjustment&lt;/li&gt;
&lt;li&gt;Work with diff = target - value instead of raw values.&lt;/li&gt;
&lt;li&gt;Stay in “distance from zero” land until done.&lt;/li&gt;
&lt;li&gt;Translate back at the very end.
(You just did this with Triple Sum Closest.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Geometry / Coordinates → Shift or Rotate Origin&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Distance between points, closest pair&lt;/li&gt;
&lt;li&gt;Translate the system so one point is (0,0).&lt;/li&gt;
&lt;li&gt;For angles/slopes, rotate so a key line is horizontal.&lt;/li&gt;
&lt;li&gt;You avoid carrying extra terms in all your calculations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Intervals / Ranges → Normalize to Start=0&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Meeting rooms, merging intervals, timeline problems&lt;/li&gt;
&lt;li&gt;Shift all intervals so the earliest start is 0.&lt;/li&gt;
&lt;li&gt;Makes overlap and gap checks easier.&lt;/li&gt;
&lt;li&gt;Often turns into simple array index math.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Percent / Ratio Problems → Use a Single Ratio Space&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Speed/distance/time, growth rate comparisons&lt;/li&gt;
&lt;li&gt;Convert to one ratio type (miles/hour, profit/unit cost).&lt;/li&gt;
&lt;li&gt;Keep all comparisons in that space until final answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Modular Arithmetic → Work Modulo Early&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Rotations, circular arrays, cyclic patterns&lt;/li&gt;
&lt;li&gt;Reduce numbers modulo n immediately.&lt;/li&gt;
&lt;li&gt;Avoid juggling big numbers and wrap-around logic later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. Graph Problems → Fix a Single Perspective&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: BFS shortest path, Dijkstra’s&lt;/li&gt;
&lt;li&gt;Store only the current cost-to-reach node instead of storing both “steps” and “path length” in separate forms.&lt;/li&gt;
&lt;li&gt;Add metadata back if needed at the end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. Counting / Frequency → Use One Aggregation Metric&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Example: Anagrams, inventory matching&lt;/li&gt;
&lt;li&gt;Convert items to one comparable form (sorted string, hash, vector of counts).&lt;/li&gt;
&lt;li&gt;Compare those forms instead of juggling raw items.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Meta-rule:&lt;/strong&gt; &lt;em&gt;Always look for a transformation that removes at least one dimension of thinking.&lt;/em&gt;&lt;br&gt;
That’s the lever that turns a problem from “too many moving parts” to “just compare this one thing.”&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Variable Naming in Algorithms for Coding Interviews</title>
      <dc:creator>heather</dc:creator>
      <pubDate>Wed, 13 Aug 2025 14:20:51 +0000</pubDate>
      <link>https://dev.to/heatherp/variable-naming-in-algorithms-for-coding-interviews-g47</link>
      <guid>https://dev.to/heatherp/variable-naming-in-algorithms-for-coding-interviews-g47</guid>
      <description>&lt;p&gt;Variable naming in algorithms is a silent skill in coding interviews. Instead of naming a variable "i", if you give it a meaningful name like "lastKnownDuplicate", it will help you keep track of the &lt;em&gt;meaning&lt;/em&gt; of that variable, like labeling storage boxes.&lt;/p&gt;

&lt;p&gt;Here are some charts and tips to help you with naming variables as you prepare (with some help from ai and Grokking the Coding Interview).&lt;/p&gt;

&lt;p&gt;🗂 Grokking Patterns + Variable Names (Quick Glance)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Pattern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Go-To Variable Names&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sliding Window&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;windowStart&lt;/code&gt;, &lt;code&gt;windowEnd&lt;/code&gt;, &lt;code&gt;windowSum&lt;/code&gt;, &lt;code&gt;charFrequency&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Two Pointers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;left&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;targetSum&lt;/code&gt;, &lt;code&gt;lastNonDuplicate&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fast &amp;amp; Slow Pointers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;slowPointer&lt;/code&gt;, &lt;code&gt;fastPointer&lt;/code&gt;, &lt;code&gt;cycleStart&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merge Intervals&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;currentInterval&lt;/code&gt;, &lt;code&gt;mergedIntervals&lt;/code&gt;, &lt;code&gt;intervalStart&lt;/code&gt;, &lt;code&gt;intervalEnd&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cyclic Sort&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;currentIndex&lt;/code&gt;, &lt;code&gt;correctIndex&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;In-place Linked List Reversal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;currentNode&lt;/code&gt;, &lt;code&gt;previousNode&lt;/code&gt;, &lt;code&gt;nextNode&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tree BFS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;queue&lt;/code&gt;, &lt;code&gt;nodeQueue&lt;/code&gt;, &lt;code&gt;currentNode&lt;/code&gt;, &lt;code&gt;levelSize&lt;/code&gt;, &lt;code&gt;levels&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tree DFS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;currentPath&lt;/code&gt;, &lt;code&gt;allPaths&lt;/code&gt;, &lt;code&gt;remainingSum&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Two Heaps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;maxHeap&lt;/code&gt;, &lt;code&gt;minHeap&lt;/code&gt;, &lt;code&gt;median&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subsets / Backtracking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;currentSubset&lt;/code&gt;, &lt;code&gt;subsets&lt;/code&gt;, &lt;code&gt;startIndex&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Modified Binary Search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;start&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt;, &lt;code&gt;mid&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Top K Elements&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;minHeap&lt;/code&gt;, &lt;code&gt;maxHeap&lt;/code&gt;, &lt;code&gt;topKElements&lt;/code&gt;, &lt;code&gt;distance&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;🗂 Grokking Patterns + Variable Names (In-Depth)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sliding Window&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Start index of window&lt;/td&gt;
&lt;td&gt;&lt;code&gt;windowStart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First element in current range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End index of window&lt;/td&gt;
&lt;td&gt;&lt;code&gt;windowEnd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Last element in current range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frequency counter&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;charFrequency&lt;/code&gt;, &lt;code&gt;numFrequency&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Use type-specific name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current sum/length&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;windowSum&lt;/code&gt;, &lt;code&gt;windowLength&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Describes what’s being aggregated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Two Pointers&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Left pointer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;left&lt;/code&gt; or &lt;code&gt;start&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Works for arrays and strings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Right pointer&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;right&lt;/code&gt; or &lt;code&gt;end&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target sum&lt;/td&gt;
&lt;td&gt;&lt;code&gt;targetSum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use when looking for sum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last valid position&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lastNonDuplicate&lt;/code&gt;, &lt;code&gt;lastSorted&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Clarifies purpose&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Fast &amp;amp; Slow Pointers&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Slow pointer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;slowPointer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Advances 1 step at a time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast pointer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fastPointer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Advances 2 steps at a time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cycle start&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cycleStart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;For cycle detection problems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Merge Intervals&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Current interval&lt;/td&gt;
&lt;td&gt;&lt;code&gt;currentInterval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Often a tuple/array &lt;code&gt;[start, end]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merged result list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mergedIntervals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plural for clarity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interval start/end&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;intervalStart&lt;/code&gt;, &lt;code&gt;intervalEnd&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Explicit meaning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Cyclic Sort&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Current index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;currentIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Avoid &lt;code&gt;i&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Correct index for value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;correctIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Makes swap logic obvious&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;In-place Linked List Reversal&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Current node&lt;/td&gt;
&lt;td&gt;&lt;code&gt;currentNode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where we are now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Previous node&lt;/td&gt;
&lt;td&gt;&lt;code&gt;previousNode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Node before current&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next node&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nextNode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Node after current&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Tree BFS&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Processing queue&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;queue&lt;/code&gt; or &lt;code&gt;nodeQueue&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;BFS uses queues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current node&lt;/td&gt;
&lt;td&gt;&lt;code&gt;currentNode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Current level size&lt;/td&gt;
&lt;td&gt;&lt;code&gt;levelSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Used to group levels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result list&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;result&lt;/code&gt;, &lt;code&gt;levels&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Use plural for collection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Tree DFS&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Current path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;currentPath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;For backtracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All paths&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allPaths&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;For collecting results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remaining sum&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remainingSum&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;For path sum problems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Two Heaps&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Small half&lt;/td&gt;
&lt;td&gt;&lt;code&gt;maxHeap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stores smaller numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large half&lt;/td&gt;
&lt;td&gt;&lt;code&gt;minHeap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stores larger numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median&lt;/td&gt;
&lt;td&gt;&lt;code&gt;median&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional if caching&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Subsets / Backtracking&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Current subset&lt;/td&gt;
&lt;td&gt;&lt;code&gt;currentSubset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Or &lt;code&gt;subset&lt;/code&gt; if short-lived&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result list&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;subsets&lt;/code&gt; or &lt;code&gt;result&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Plural for clarity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;startIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;For recursive calls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Modified Binary Search&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Search start&lt;/td&gt;
&lt;td&gt;&lt;code&gt;start&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lower bound index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search end&lt;/td&gt;
&lt;td&gt;&lt;code&gt;end&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upper bound index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Midpoint&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Middle index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What we’re searching for&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Top K Elements&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Good Name&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Heap&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;minHeap&lt;/code&gt; or &lt;code&gt;maxHeap&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Match name to type used&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;topKElements&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Describes final goal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distance function&lt;/td&gt;
&lt;td&gt;&lt;code&gt;distance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;For closest point problems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;🗂 Grokking Patterns + Variable Names (Quick Tips)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Role + Context&lt;br&gt;
Instead of i, say what it is and why it’s there.&lt;/p&gt;

&lt;p&gt;❌ i → ✔ windowStart&lt;br&gt;
❌ j → ✔ windowEnd&lt;br&gt;
❌ count → ✔ charFrequency&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Relationship to Other Variables&lt;br&gt;
If there’s a pair, name them so they “talk to each other.”&lt;/p&gt;

&lt;p&gt;fastPointer / slowPointer&lt;br&gt;
minHeap / maxHeap&lt;br&gt;
left / right&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Avoid Mental Gymnastics&lt;br&gt;
If a name makes you stop and translate it in your head, it’s too short or too vague.&lt;/p&gt;

&lt;p&gt;❌ ptr → ✔ currentNode&lt;br&gt;
❌ n1, n2 → ✔ intervalStart, intervalEnd&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Deploying a fastapi/vite/React Router application with Docker in AWS ec2</title>
      <dc:creator>heather</dc:creator>
      <pubDate>Mon, 11 Aug 2025 16:16:56 +0000</pubDate>
      <link>https://dev.to/heatherp/deploying-a-fastapivitereact-router-application-with-docker-in-aws-ec2-10p</link>
      <guid>https://dev.to/heatherp/deploying-a-fastapivitereact-router-application-with-docker-in-aws-ec2-10p</guid>
      <description>&lt;p&gt;I wanted to deploy a modern python + react application in the easiest possible way. My application uses:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "@tailwindcss/vite": "^4.1.11",
    "react": "^19.1.0",
    "react-dom": "^19.1.0",
    "react-router": "^7.7.1",
    "react-router-dom": "^7.7.1",
    "tailwindcss": "^4.1.11"
 "vite": "^7.0.6"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt;&lt;br&gt;
fastapi&lt;br&gt;
uvicorn[standard]&lt;br&gt;
sqlalchemy&lt;br&gt;
pydantic&lt;br&gt;
chromadb&lt;/p&gt;

&lt;p&gt;I had been developing in pycharm using a venv. That is stable and predictable, but does not allow for CD (continuous deployment) which is what I wanted. &lt;/p&gt;

&lt;p&gt;So it was time to deploy, I switched to:&lt;br&gt;
docker&lt;br&gt;
docker-compose&lt;br&gt;
caddy&lt;/p&gt;

&lt;p&gt;Here's how I did it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My application layout:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── backend
│   ├── auth_providers
│   ├── config
│   ├── data
│   ├── db
│   ├── db.py
│   ├── Dockerfile
│   ├── main.py
│   ├── models
│   ├── __pycache__
│   ├── requirements.txt
│   ├── routers
│   ├── server.js
│   ├── services
│   ├── tests
│   └── utils
├── Caddyfile.dev
├── Caddyfile.prod
├── docker-compose.override.yml
├── docker-compose.prod.yml
├── docker-compose.yml
├── frontend
│   ├── dist
│   ├── Dockerfile
│   ├── index.html
│   ├── node_modules
│   ├── package.json
│   ├── package-lock.json
│   ├── public
│   ├── src
│   └── vite.config.js
├── package.json
├── package-lock.json
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I decided to use caddy because, as long as you have a domain name mapped to your aws instance it provides an ssl certificate for that domain using Let's Encrypt. It all truly "just works". For someone who spent years configuring certificates in Apache, this is amazing! &lt;/p&gt;

&lt;h3&gt;
  
  
  PREPARING FOR DEPLOYMENT
&lt;/h3&gt;

&lt;p&gt;Transitioning from my .venv environment to a docker container on my localhost was challenging. &lt;/p&gt;

&lt;p&gt;In my .venv environment, I had written my app with this straightforward structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myapp
├── backend
├── frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But with Docker I changed the architecture to three different containers governed by a docker-compose.yml file:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;backend container:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myapp
├── backend
├── frontend_dist
├── requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;frontend container (builds asssets served by caddy. Not needed in prod):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myapp
├── src
├── dist
├── vite.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;caddy (web server) container (serves assets in prod):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;srv
├── assets
├── index.html
├── manifest.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So a lot of work just changing paths and ports. I could have prevented this by containerizing with Docker from the get-go. Oh well, 20-20 hindsight!&lt;/p&gt;




&lt;h3&gt;
  
  
  TROUBLESHOOTING:
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Here are some issues and solutions I encountered.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ISSUE:&lt;/strong&gt; docker compose up returns this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api       | ModuleNotFoundError: No module named 'backend'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt; Because I was using /app for the docker environment as opposed to /myapp in my .venv environment, i had to add "app" to my &lt;code&gt;PYTHONPATH&lt;/code&gt; in backend/Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WORKDIR /app
ENV PYTHONPATH=/app
COPY backend /app/backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then when I mounted /backend to /app/backend in container from docker-compose.yml, the paths in main.py made sense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ISSUE:&lt;/strong&gt; Anything other than an exact url match resulted in a 404 error. &lt;/p&gt;

&lt;p&gt;'&lt;a href="http://mysite/login" rel="noopener noreferrer"&gt;http://mysite/login&lt;/a&gt;' worked&lt;br&gt;
'&lt;a href="http://mysite/bla" rel="noopener noreferrer"&gt;http://mysite/bla&lt;/a&gt;' gave 404 error&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt; I needed redirect, so used a react catch-all route, the &lt;code&gt;path="*"&lt;/code&gt; route ie:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;Routes&amp;gt;
      &amp;lt;Route path="/login" element={&amp;lt;Login /&amp;gt;} /&amp;gt;
      &amp;lt;Route
        path="/"
        ...
      /&amp;gt;
      &amp;lt;Route path="*" element={&amp;lt;Navigate to="/" replace /&amp;gt;} /&amp;gt;
&amp;lt;/Routes&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, configure Caddyfile like this with &lt;code&gt;try_files&lt;/code&gt; before &lt;code&gt;file_server&lt;/code&gt;, inside &lt;code&gt;handle {}&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;handle {
    try_files {path} /index.html
    file_server
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ISSUE:&lt;/strong&gt; &lt;code&gt;npm run dev&lt;/code&gt; gives this on startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: crypto.hash is not a function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt; Be sure to work with node 20 (or 18+) which has upgraded Crypto API. So instead of &lt;code&gt;FROM node:18-bullseye&lt;/code&gt;, use &lt;code&gt;FROM node:20&lt;/code&gt; in your frontend Dockerfile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ISSUE:&lt;/strong&gt; &lt;code&gt;"RuntimeError: Directory '../frontend/dist/assets' does not exist"&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;SOLUTION:&lt;/strong&gt; in /app/main.py  I had: &lt;code&gt;app.mount("/static/assets", StaticFiles(directory="../frontend/dist/assets"), name="static")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I had to define an env variable in docker-compose.yml and access it in main.py to determine whether I was in local architecture or docker container. I then used it to create a prefix which I used to build paths.&lt;br&gt;
docker-compose.yml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    environment:
      - APP_ENV=docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;main.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ENV = os.getenv("APP_ENV", "local")

if ENV == "docker":
    path_to_dist = Path('/app/frontend_dist')
else:
    path_to_dist = Path(__file__).resolve().parent.parent / 'frontend' / 'dist'

path_to_assets = path_to_dist / 'assets'
path_to_index = path_to_dist / 'index.html'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HINT:&lt;/strong&gt;* digging around in my running containers helped a lot when trying to see where things were mounted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker exec -it &amp;lt;mycontainer&amp;gt; sh
docker exec -it &amp;lt;mycontainer&amp;gt; bash #bash is better, install it when you can
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HINT:&lt;/strong&gt; my .dockerignore contained /frontend_dist, causing it not to be seen. Always check your .dockerignore. Here are the dockerignore entries I did need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.venv/
__pycache__
*.pyc
node_modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ISSUE:&lt;/strong&gt; docker ran, but I got a blank screen and this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Loading module from “http://localhost:5000/static/assets/index-CaU4slfD.js” was blocked because of a disallowed MIME type (“”). localhost:5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SOLUTION:&lt;/strong&gt; In docker, I was trying to mount assets like this: &lt;code&gt;/app/frontend_dist/assets/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;but main.py had: &lt;code&gt;app.mount("/static", StaticFiles(directory=FRONTEND_DIST), name="static")&lt;/code&gt; causing assets to be mounted from wrong place.&lt;/p&gt;

&lt;p&gt;So I changed main.py to: &lt;code&gt;app.mount("/assets", StaticFiles(directory=path_to_dist), name="assets")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and in vite.config.js I switched to this "base" value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default defineConfig({
 base: "/",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ISSUE:&lt;/strong&gt; &lt;code&gt;405 error (Method Not Allowed)&lt;/code&gt; &lt;br&gt;
&lt;strong&gt;SOLUTION:&lt;/strong&gt; &lt;br&gt;
The problem was that my ports were not aligned, between &lt;code&gt;backend/Dockerfile&lt;/code&gt;, &lt;code&gt;frontend/Dockerfile&lt;/code&gt;, &lt;code&gt;docker-compose.yml&lt;/code&gt;, and &lt;code&gt;Caddyfile&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This can be challenging when trying to handle the transition from .venv to Docker, while maintaining different environments for dev and production. &lt;/p&gt;

&lt;p&gt;These tips are not bulletproof security but may help you get unstuck from 405 errors.&lt;/p&gt;

&lt;p&gt;This is what I needed: &lt;/p&gt;

&lt;p&gt;*&lt;code&gt;caddy&lt;/code&gt; serving on port 80 in the container, and 443 in production.&lt;br&gt;
*&lt;code&gt;frontend&lt;/code&gt; hot-reloading on 5173 for testing.&lt;br&gt;
*&lt;code&gt;api&lt;/code&gt; Caddy reverse-proxies from port 5000 (host) to api:8000 (container), for dev.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caddy (dev): host :5000 → Caddy :80 (in container).&lt;br&gt;
*Caddy (prod): :443 with TLS.&lt;br&gt;
*Vite (dev): :5173 with hot reload.&lt;br&gt;
*Reverse proxy (dev): Caddy proxies /api&lt;/em&gt; → api:8000.&lt;/p&gt;

&lt;p&gt;vite.config.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server: {
  proxy: {
    '/api1': 'http://localhost:5000', // adjust to your backend
    '/api2': 'http://localhost:5000', 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caddyfile (dev environment only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;handle /api1/* {
        reverse_proxy api:8000
    }
handle /api2/* {
        reverse_proxy api:8000
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;HINT:&lt;/strong&gt; If you include the path such as "api1" in your route, use &lt;code&gt;handle&lt;/code&gt;. If you want it stripped, use &lt;code&gt;handle_path&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;docker-compose (dev environment):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  api:    
    ports:
      - "8000:8000"

  caddy:
    ports:
      - "5000:80"

  frontend:
    build:
      context: .
      dockerfile: frontend/Dockerfile
    container_name: frontend
    ports:
      - "5173:5173"   # default Vite dev server port for testing and hot-reloading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a simple dev/prod diagram for ports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Dev:
Browser → Vite :5173
  └─ proxy /api1,/api2 → Caddy :5000 → reverse_proxy → api:8000

Prod:
Browser → Caddy :443
  ├─ / (static Vite build)
  └─ /api → reverse_proxy → api:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ALSO REMEMBER:&lt;/strong&gt; These issues are usually not caused by CORS, so look elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ISSUE:&lt;/strong&gt; Could not access my api2.&lt;br&gt;
&lt;strong&gt;SOLUTION:&lt;/strong&gt; All apis needed to be added as proxy in vite.config.js, ie:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server: {
  proxy: {
    '/api': 'http://localhost:5000',
    '/api2': 'http://localhost:5000' #the one that I could not access.
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ISSUE:&lt;/strong&gt; Needed different ports and values for dev/prod. &lt;br&gt;
&lt;strong&gt;SOLUTION:&lt;/strong&gt; I wound up splitting up my docker-compose and Caddyfiles. Here's an example of how I called them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose -f docker-compose.yml -f docker-compose.prod.yml up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  FINAL TIPS
&lt;/h3&gt;

&lt;p&gt;It may be no surprise that I used AI extensively for this project. Here are some final tips from GPT-4 that I thought were particularly useful:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Final Tips&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Recommended?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run FastAPI directly from .venv&lt;/td&gt;
&lt;td&gt;❌ Not when testing Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep .venv in project root&lt;/td&gt;
&lt;td&gt;✅ (but add it to .dockerignore)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use Docker for full-stack testing&lt;/td&gt;
&lt;td&gt;✅ Definitely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use uvicorn outside Docker&lt;/td&gt;
&lt;td&gt;Only for quick local debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;&lt;strong&gt;DOCKER COMMANDS&lt;/strong&gt; that I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose build --no-cache
docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  FURTHER READING
&lt;/h3&gt;

&lt;p&gt;Here is a good &lt;a href="https://blog.shellnetsecurity.com/2023/04/574/general/using-docker-instead-of-virtual-environments-venv/#google_vignette" rel="noopener noreferrer"&gt;high-level writeup&lt;/a&gt; on why you may want to use docker instead of venv.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setting up config variables in a Flask app</title>
      <dc:creator>heather</dc:creator>
      <pubDate>Sat, 28 Jun 2025 20:57:49 +0000</pubDate>
      <link>https://dev.to/heatherp/setting-up-config-variables-in-a-flask-app-3iek</link>
      <guid>https://dev.to/heatherp/setting-up-config-variables-in-a-flask-app-3iek</guid>
      <description>&lt;p&gt;Here is how I set up configuration files in my Flask app.&lt;/p&gt;

&lt;h2&gt;
  
  
  app.config object
&lt;/h2&gt;

&lt;p&gt;my_app/config.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
basedir = os.path.abspath(os.path.dirname(__file__))

class Config(object):
    SQLALCHEMY_DATABASE_URI = 'sqlite:///test_whatever.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = 'sqlite:///prod_whatever.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevelopmentConfig(Config):
    SQLALCHEMY_DATABASE_URI = 'sqlite:///test_whatever.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;my_app/app/app.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from flask import Flask, send_from_directory
from config import ProductionConfig, Config, DevelopmentConfig
from app.models import db
import os

def create_app():
    app = Flask(__name__)

    # Here, you refer to the DevelopmentConfig as a string
    app.config.from_object('config.DevelopmentConfig')

    # more init stuff here, blueprints, etc

    db.init_app(app)
    return app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  environment variables
&lt;/h2&gt;

&lt;p&gt;Sometimes you might want to load environment variables instead. For example to avoid the whole circular dependency issue when initializing blueprints within your app. &lt;/p&gt;

&lt;p&gt;Here's how I initialized and read environment variables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;in venv pip install python-dotenv&lt;/li&gt;
&lt;li&gt;include .env file at application root. Format should be MY_VAR='whatever'&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;in my_app/app/routes.py (or whatever .py file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from dotenv import load_dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can initialize and refer explicitly to the environment vars:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;load_dotenv()  # take environment variables
api_key=os.getenv('MY_API_KEY') #refer to environment variable
print('api key: ', api_key)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;openai is a special case, they don't want it passed in explicitly. This worked for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;load_dotenv()  # take environment variables
openai = OpenAI()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find a &lt;a href="https://flask.palletsprojects.com/en/stable/config/" rel="noopener noreferrer"&gt;more exhaustive guide to configuration here&lt;/a&gt;. Hopefully this will get you started.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
