<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Learning Notes]]></title><description><![CDATA[A space where I document pieces of learning about concepts, software, infrastructure, systems, and myself as they happen. Think of it as "Behind Technical Growt]]></description><link>https://oyindanotes.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 18 Jun 2026 22:39:39 GMT</lastBuildDate><atom:link href="https://oyindanotes.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Raw Learning Notes: Arrays, Slices, and Loops]]></title><description><![CDATA[These are messy thinking notes, not a tutorial. This is what I understood, struggled with, and also realized.
About Arrays
Arrays and slices hold values, but programs interact with them through indexe]]></description><link>https://oyindanotes.hashnode.dev/raw-learning-notes-arrays-slices-and-loops</link><guid isPermaLink="true">https://oyindanotes.hashnode.dev/raw-learning-notes-arrays-slices-and-loops</guid><dc:creator><![CDATA[Oyindamola Abiola]]></dc:creator><pubDate>Sun, 15 Mar 2026 01:49:49 GMT</pubDate><content:encoded><![CDATA[<p>These are messy thinking notes, not a tutorial. This is what I understood, struggled with, and also realized.</p>
<h2>About Arrays</h2>
<p>Arrays and slices hold <strong>values</strong>, but programs interact with them through <strong>indexes (positions)</strong></p>
<p>Example</p>
<pre><code class="language-go">index: 0 1 2 3
value: 4 7 2 9
</code></pre>
<p>Programs don't understand values first, they understand <strong>positions</strong>.</p>
<p>So basically every algorithm is</p>
<ul>
<li><p>move across indexes (left → right or other way round)</p>
</li>
<li><p>look at the value at that position</p>
</li>
<li><p>compare</p>
</li>
<li><p>update something</p>
</li>
<li><p>continue… depending on what needs to be done</p>
</li>
</ul>
<p>That is literally the pattern I noticed in almost every exercise I did.</p>
<h2>Array Structure</h2>
<p>Example</p>
<pre><code class="language-go">arr := [5]int{10, 20, 30, 40, 50}
</code></pre>
<p>What I understand now:</p>
<ul>
<li><p>arrays have fixed size</p>
</li>
<li><p>they cannot grow</p>
</li>
<li><p>cannot shrink</p>
</li>
<li><p>the size is part of the type</p>
</li>
</ul>
<p><em>So this[5]int means it's type array, and can never be [6]int or anything else</em></p>
<p>The memory of it looks like</p>
<pre><code class="language-go">[10][20][30][40][50]
</code></pre>
<p>Values can change in position, but you cannot add or remove positions.</p>
<p>Example</p>
<pre><code class="language-go">arr[2] = 99 (index 2 to be 99)
[10][20][99][40][50]
</code></pre>
<p>Array seems pretty straightforward.</p>
<h2>Slice</h2>
<p>Slices are basically views into an array. I used to think slices were just flexible arrays, but now I understand that they are actually a window/interface into an array.</p>
<p>Example</p>
<pre><code class="language-go">s := []int{10, 20, 30}
// The [] means slice.
</code></pre>
<p>Internally, slice has:</p>
<ul>
<li><p>Pointer: where the slice starts in memory</p>
</li>
<li><p>Length: how many elements it currently show</p>
</li>
<li><p>Capacity: how far they can grow</p>
</li>
</ul>
<h2>Slice From Array</h2>
<p>Example</p>
<pre><code class="language-go">arr := [5]int{10, 20, 30, 40, 50}
slc := arr[1:4]
</code></pre>
<p>Slice becomes</p>
<pre><code class="language-go">*// start from index 1, count till the 4th item*
[20 30 40]

/* 
	T*he slice did not start counting from array index 1.*
	*Slice index resets.
*/* 
arr[1] -&gt; slc[0] -&gt; 20
arr[2] -&gt; slc[1] -&gt; 30
arr[3] -&gt; slc[2] -&gt; 40
</code></pre>
<p>I also noticed a shared memory when I did:</p>
<pre><code class="language-go">slc[0] = 999
</code></pre>
<p>For array, it became:</p>
<pre><code class="language-go">arr := [10][999][30][40][50]
</code></pre>
<p>Seriously confused and surprised because modifying the slice actually modified the array. Meaning that slices share memory with the array, they don’t just copy.</p>
<p>changed the array:</p>
<pre><code class="language-go">[10][999][30][40][50]
</code></pre>
<p>Slices share memory with arrays.</p>
<h2>Append Behavior</h2>
<p>Append is interestingly funny, I thought it only added items. If the slice has extra capacity, the append writes into the array.</p>
<p>Example I tried:</p>
<pre><code class="language-go">arr := [6]int{10, 20, 30, 40, 50, 60}
slc := arr[0:4]

slc = append(slc, 70)

// Array becomes
[10 20 30 40 70 60]
</code></pre>
<p>70 replaced 50. This, too, surprised me…</p>
<p>This happens because slice still had capacity left inside the array, so Go just used the next slot in the slice, and since slice modifies into its array, it changes the array. But if capacity is full, Go secretly creates a new array, copies values, and then appends. So sometimes slices share memory, sometimes they detach, tricky but interesting.</p>
<h3>Length and Capacity</h3>
<p>len(slice) = number of elements</p>
<p>cap(slice) = maximum usable space before reallocation.</p>
<p>Example</p>
<pre><code class="language-go">slc := arr[0:4]

// If array has 6 elements:
len(slc) = 4
cap(slc) = 6
</code></pre>
<p>Because slice can extend until end of array.</p>
<h2>Problems</h2>
<ol>
<li>Check if Slice is Non-Decreasing</li>
</ol>
<pre><code class="language-go">arr9 := []int{2, 4, 4, 7, 9}
	/* 
		assume the data is non-decreasing (the curr &gt; or = prev)
		assume true until proven false
	*/
	nonDec := true

	for n := 1; n &lt; len(arr9); n++ {
		if arr9[n] &lt; arr9[n-1] {
			nonDec = false
			break
		}
	}
	fmt.Println(nonDec)
</code></pre>
<p>My approach:</p>
<ul>
<li><p>assume nonDec = true</p>
</li>
<li><p>scan array</p>
</li>
<li><p>if current &lt; previous -&gt; false</p>
</li>
</ul>
<p>Classic loop was used because I needed access to previous element, and the range loop does not easily give that.</p>
<p>The challenge I had was understanding why the loop started at index 1, because index 0 has no previous element.</p>
<ol>
<li>Strictly Increasing</li>
</ol>
<pre><code class="language-go">Check if a Slice Is strictly increasing
	arr10 := []int{2, 4, 4, 7, 9}
	// assume the array is strictly increasing (curr &gt; prev)
	strInc := true

	for o := 1; o &lt; len(arr10); o++ {
		if arr10[o] &lt;= arr10[o-1] {
			strInc = false
			break
		}
	}
	fmt.Println(strInc)
</code></pre>
<p>Logic - current must be &gt; previous</p>
<p>Check - <code>arr10[i] &lt;= arr10[i-1]</code></p>
<p>Challenge was understanding the difference between &gt;=, &gt;, &lt;=, and &lt; for sorted rules.</p>
<hr />
<ol>
<li>Detect Sorted (Ascending / Descending / Not Sorted)</li>
</ol>
<pre><code class="language-go">/* 
	Check if the array is sorted (ascending) – Return true if sorted, otherwise false.
*/
	// arr6 := []int{1, 2, 3, 5, 4, 6}
	arr6 := []int{10, 20, 15, 30}
	sorted := true // assume true until proven otherwise

	// start from index 1 not zero so that i-1 will be within the range
	for i := 1; i &lt; len(arr6); i++ {
		if arr6[i] &lt; arr6[i-1] {
			sorted = false
			break
		}
	}

	fmt.Println(sorted)

	// for-range version
	for ind := range arr6 { // ignore values, not index
		if ind == 0 {
			continue
		}
		if arr6[ind] &lt; arr6[ind-1] {
			sorted = false
			break
		}
	}

	fmt.Println(sorted)
</code></pre>
<p>As I mentioned earlier :</p>
<pre><code class="language-go">current &gt; previous -&gt; ascending
current &lt; previous -&gt; descending
mix -&gt; not sorted
</code></pre>
<p>This requires neighbour comparison, so classic loop it is.</p>
<hr />
<ol>
<li>Find Largest Number</li>
</ol>
<pre><code class="language-go">	// Find the largest odd number
	arr12 := []int{12, 7, 19, 3, 19, 5}
	lgst := arr12[0]

	for q := 0; q &lt; len(arr12); q++ {
		if arr12[q] &gt;= lgst &amp;&amp; arr12[q] % 2 == 0 { // largest even
			lgst = arr12[q]
		}
	} 
	fmt.Println(lgst)
	
// for-range version
	for val := range arr12 {
		if arr12[val] &gt;= lgst &amp;&amp; arr12[val] % 2 == 1 { // largest odd
			lgst = arr12[val]
		}
	}
	fmt.Println(lgst)
</code></pre>
<p>Running comparison pattern is classic algorithm pattern, I did both classic loop and range loop. Range worked only because the values were compare.</p>
<pre><code class="language-go">largest item = arr12[0]
scan rest
if current &gt;= largest
update largest
</code></pre>
<ol>
<li>Find Second Largest (Distinct)</li>
</ol>
<pre><code class="language-go">// Find the second‑largest element (invariant)
	arr7 := []int{12, 7, 19, 3, 19, 5}

	// assume the indices of max and second max
	maxx := arr7[0]
	secMax := arr7[1]

	for i := 0; i &lt; len(arr7); i++ {
		if arr7[i] &gt; maxx {
			secMax = maxx
			maxx = arr7[i]
		} else if arr7[i] &lt; maxx &amp;&amp; arr7[i] &gt; secMax {
			secMax = arr7[i]
		}
	}
	fmt.Println("Second Largest: ", secMax)

	// Deduplicate an array
	arr8 := []int{4, 2, 4, 6, 2, 8, 6}
	arrMap := make(map[int]bool) // for validation
	var newArr8 []int

	for _, m := range arr8 {
		if !arrMap[m] {
			newArr8 = append(newArr8, m)
			arrMap[m] = true
		} 
	}
	fmt.Println("New Array: ", newArr8)
</code></pre>
<p>Idea:</p>
<pre><code class="language-go">largest = arr7[0]
secondLargest = arr7[1]

if current &gt; largest
    second = largest
    largest = current
    
if current &lt; largest AND current &gt; second
    second = current
</code></pre>
<p>My challenge here was <em>“what if there are duplicates?”. “What happens when first many indexes are same value?”</em></p>
<p>Example:</p>
<p><code>[9,7,9,8] [4, 4, 4, 4, 4, 6, 1, 8, 0]</code></p>
<p>The solution is to never assume unique first two values, also learned about initializing the second value properly (don’t understand it yet)</p>
<ol>
<li>Remove negatives, new slice and in-place</li>
</ol>
<pre><code class="language-go">	// Remove all negatives (new slice)
	arr5 := []int{-3, 5, -1, 0, 8, -2, 7}
	var nonNegArr5 []int

	for _, j := range arr5 {
		if j &gt;= 0 {
			nonNegArr5 = append(nonNegArr5, j)
		}
	}
	fmt.Println("Non-negative = ", nonNegArr5)

	// Remove all negatives in place with while-style loop
	arrr := []int{-3, 5, -1, 0, 8, -2, 7} 
	j := 0

	for j &lt; len(arrr) {
		if arrr[j] &lt; 0 { 
			j++
			continue
		}
		fmt.Println(arrr[j])
		j++
	}
</code></pre>
<p>The goal was to create slice with only non-negative numbers.</p>
<ul>
<li><p>When <code>arr[j] &lt; 0</code>, the <code>continue</code> skips printing, and goes back to check <code>j &lt; len(arr)</code>.</p>
</li>
<li><p>Because <code>j++</code> is before continue, the loop moves to the next instead of looping infinitely on the same index.</p>
</li>
<li><p>This made me understand why the while-style loop needs manual control of the index.</p>
</li>
</ul>
<p>The appropriate loop is Range-Loop, because it was just to scan values</p>
<pre><code class="language-go">*if value &gt;= 0, then
append*
</code></pre>
<p>Here, the issue I faced with the “while-loop version” made me understand “<code>j++</code>” must come before the “continue”, why that is, and how “continue” works internally.</p>
<p><code>*continue</code> doesn’t mean “skip this part and then move forward before updating the index,” as I used to think; it immediately ends the current iteration and jumps back to the loop condition, and then moves to the next iteration. It stops executing the rest of the code inside the loop after the <code>continue</code>, and moves to the next iteration. If you don’t manually update the index before it, the loop gets stuck*</p>
<p><code>continue -&gt; stop executing remaining code in loop -&gt; jump to the loop's next iteration</code></p>
<pre><code class="language-go">for i := 0; i &lt; 5; i++ {
	fmt.Println("A")
	continue

	fmt.Println("B")
}

// prints "A" 5times, "B" never print
// fmt.Println("B") is skipped.
</code></pre>
<p>This also made me understand that:</p>
<ul>
<li><p>Classic for loop gives full index control → perfect for in-place, neighbor index slice modifications.</p>
</li>
<li><p>Range loop automatically handles iteration → perfect for when the problem only care about values, not modifying the indexes.</p>
</li>
<li><p>Misplacing <code>continue</code> or forgetting manual index in a while-style loop → leads to skipped items, infinite loops, or being stuck.</p>
</li>
</ul>
<h3>I tried removing the negatives in place using the same code example</h3>
<pre><code class="language-go">	// Remove all negatives in place with while-style loop
	arrr := []int{-3, 5, -1, 0, 8, -2, 7} 
	j := 0

	for j &lt; len(arrr) {
		if arrr[j] &lt; 0 { 
			j++
			continue
		}
		fmt.Println(arrr[j])
		j++
	}
</code></pre>
<p>What I did was:</p>
<ul>
<li><p>remove the element if it’s negative</p>
</li>
<li><p>let the rest shift left automatically</p>
</li>
<li><p>use <code>continue</code> to skip the remaining</p>
</li>
</ul>
<p>I came to see that</p>
<ul>
<li><p>repeated shifting is inefficient</p>
</li>
<li><p>every removal moves all elements after it, which will be slow for big slices</p>
</li>
<li><p>it is hard to track indexes, and it is easy to skip elements or get into infinite loop</p>
</li>
</ul>
<p>Better approach is to</p>
<ul>
<li>use overwrite and truncate, which I am currently trying to understand, my brain feels tired right now!!</li>
</ul>
<h2>Loop Types in Go</h2>
<h3>Classic For Loop (Index Control)</h3>
<p>This gives full control over the index and value, perfect for neighbor comparisons, in-place modifications, skipping items, backward iteration, etc…</p>
<pre><code class="language-go">for i := 0; i &lt; len(arr); i++ {
}
</code></pre>
<p>Classic Index Loop:</p>
<ul>
<li><p>used when index control is needed like skip, jump, backwards</p>
</li>
<li><p>when need to compare previous/next element</p>
</li>
<li><p>need to modify element in-place, eg reverse positioning, insert</p>
</li>
</ul>
<p>Example</p>
<pre><code class="language-plaintext">arr[i] vs arr[i-1]
</code></pre>
<p>Range loop cannot easily do this.</p>
<h3>Range Loop (Consumer Loop)</h3>
<p>For-Range is mostly a consumer loop in order; it takes in and processes existing data or works on existing data. Perfect for when just values are needed, no shifting or neighbor comparisons.</p>
<pre><code class="language-go">for index, value := range arr {
}
/* 
	it iterate from left to right
	treates items as READ-ONLY
	visit each element exactly once
*/
</code></pre>
<p>For-Range cannot access neighbor items, like in ordering or sorting existing data. Like checking about the previous or next element, it cannot change the data position by itself. It only cares about the current and moving to the next and the next. It is PERFECT for data, where each element can be processed independently</p>
<ul>
<li><p>used when index is not needed</p>
</li>
<li><p>when just reading the values</p>
</li>
<li><p>used when clarity is needed</p>
</li>
<li><p>cannot manipulate data</p>
</li>
<li><p>it consumes items in order</p>
</li>
</ul>
<h3>While-Style Loop in Go</h3>
<p>While loop does not do automatic increment, code gets into infinite loops if you forget <code>i++</code>.</p>
<pre><code class="language-go">for input != "quit" {
}
</code></pre>
<ul>
<li><p>use when reading user input</p>
</li>
<li><p>when scanning streams</p>
</li>
<li><p>good for state changes</p>
</li>
<li><p>when unknown iteration counts</p>
</li>
</ul>
<p>For arrays &amp; slices in Go, classic and range loops are preferable, because classic for gives the same flexibility, and it’s safer.</p>
<h2>The Things I learned</h2>
<ul>
<li><p>Slices share memory with arrays.</p>
</li>
<li><p>Append can modify original array.</p>
</li>
<li><p>Most array problems are pattern recognition.</p>
</li>
<li><p>What each loop type are best for.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Control Flow Notes]]></title><description><![CDATA[Challenges:

Not knowing what kind of problem I was solving.

I can think of possible ways and ideas to solve problems, but putting them together in a logical sequence is the challenge.

Mental pressure to solve, trying to code before having clarity
...]]></description><link>https://oyindanotes.hashnode.dev/control-flow-notes</link><guid isPermaLink="true">https://oyindanotes.hashnode.dev/control-flow-notes</guid><category><![CDATA[control flow]]></category><category><![CDATA[for loop]]></category><category><![CDATA[golang]]></category><category><![CDATA[Golang developer]]></category><dc:creator><![CDATA[Oyindamola Abiola]]></dc:creator><pubDate>Thu, 04 Dec 2025 23:00:00 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-challenges"><strong>Challenges</strong>:</h3>
<ul>
<li><p>Not knowing what kind of problem I was solving.</p>
</li>
<li><p>I can think of possible ways and ideas to solve problems, but putting them together in a logical sequence is the challenge.</p>
</li>
<li><p>Mental pressure to solve, trying to code before having clarity</p>
</li>
<li><p>Learning tools without concepts</p>
</li>
</ul>
<h3 id="heading-what-i-learnt"><strong>What I learnt</strong></h3>
<p>Control flow is basically about <strong>decision + repetition</strong></p>
<ul>
<li><p>loop (for / while-style) = tracking, repetition</p>
</li>
<li><p>conditions (if / else, <code>true</code> or <code>false</code>)</p>
</li>
<li><p>continue/break = when to skip or stop</p>
</li>
<li><p>nesting (loops inside loops)</p>
</li>
<li><p>state = memory</p>
</li>
<li><p>decision = transition</p>
</li>
</ul>
<p><strong>Also learned how to:</strong></p>
<ul>
<li><p>pause/calm before coding</p>
</li>
<li><p>classify the problem</p>
</li>
<li><p>choose tools intentionally (flexible)</p>
</li>
<li><p>separate problem type from syntax, they are not the same.</p>
</li>
</ul>
<h3 id="heading-3-core-control-flow-moves">3 Core Control Flow Moves</h3>
<p><strong>Sequence:</strong> Running instructions from <strong>top to bottom</strong>.</p>
<p><strong>Decision (Branching):</strong> “If this is true, do A. Otherwise, do B.”</p>
<blockquote>
<p><em>I realize that</em> <code>if</code> does <strong>not belong to loops,</strong> it can exist anywhere (outside or inside), loops just repeat decisions, thats all.</p>
</blockquote>
<p><strong>Repetition (Loops):</strong> Keep doing this while a condition is true.</p>
<h3 id="heading-types-of-loops">Types of Loops</h3>
<ul>
<li>Classic <code>for</code> Loop: when I know the count, how many times the program should run.</li>
</ul>
<p>Examples are print 1 to 10, iterate over array indices, fixed grid sizes</p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i &lt;= <span class="hljs-number">10</span>; i++ {
     fmt.Println(i)
}
</code></pre>
<ul>
<li><p>i = starts</p>
</li>
<li><p>condition is checked</p>
</li>
<li><p>body runs</p>
</li>
<li><p>i changes</p>
</li>
<li><p>repeat</p>
</li>
</ul>
<p>If the condition is false at any point, loop stops.</p>
<pre><code class="lang-go">i == max is WRONG, == means *only when exactly equal*
i &lt;= max is CORRECT, &lt;= means *until it reaches*
</code></pre>
<ul>
<li>While-Style Loop: when I don’t know the count, or how many times the program will run or user will input</li>
</ul>
<p>Examples are:</p>
<p>user input, password attempts, read until <code>0</code>, read until <code>"done"</code></p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> n != <span class="hljs-number">0</span> {
     fmt.Scan(&amp;n)
}
</code></pre>
<p>With while-loop, there is no counter, no increment section, only a <strong>condition.</strong> It’s <strong>more flexible</strong>.</p>
<h3 id="heading-control-flow-patternsconcepts"><strong>Control Flow Patterns/Concepts</strong></h3>
<ol>
<li><strong>Counter Loop: How many times, used when the number of iterations matters more than the values.</strong></li>
</ol>
<p><strong>Thought process</strong></p>
<ul>
<li><p>Something to happen in a fixed number of times, it’s about the count</p>
</li>
<li><p>A counter starts somewhere, usually 0 or 1</p>
</li>
<li><p>It changes predictably</p>
</li>
<li><p>The loop ends when the count is reached</p>
</li>
</ul>
<p><strong>Example task</strong></p>
<ul>
<li><p>Print the first 10 numbers</p>
</li>
<li><p>Print 8 odd numbers</p>
</li>
<li><p>Repeat an action 5 times</p>
</li>
<li><p>Print the first 8 odd numbers.</p>
</li>
</ul>
<p><strong>Reasoning</strong></p>
<ul>
<li><p>I need 8 outputs → counter</p>
</li>
<li><p>Odd numbers → start at 1</p>
</li>
<li><p>Next odd = +2</p>
</li>
<li><p>Stop after 8 prints</p>
</li>
</ul>
<p><strong>Code for (Print the first 8 odd numbers.)</strong></p>
<pre><code class="lang-go">count := <span class="hljs-number">0</span>

<span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; count &lt; <span class="hljs-number">8</span>; i += <span class="hljs-number">2</span> {
        fmt.Println(i)
        count++
}
</code></pre>
<p><strong>Note:</strong> loop variable (i) and the reason for stopping (counter) do NOT have to be the same thing, because an external counter is required here to get cleaner output.</p>
<ol>
<li><strong>Sentinel Loop: Run until something happens. Important concept in While-loop.</strong></li>
</ol>
<p><strong>Thought process</strong></p>
<ul>
<li><p>Don’t know how many times, stop when there’s a signal.</p>
</li>
<li><p>The signal is the sentinel, can 0, negative number, "stop", “done”, etc.</p>
</li>
<li><p>Read input</p>
</li>
<li><p>Check sentinel condition</p>
</li>
<li><p>Continue until sentinel appears</p>
</li>
<li><p>What matters is not the loop, it’s the <strong>sentinel condition</strong>.</p>
</li>
</ul>
<p><strong>Example task</strong></p>
<p>Read numbers until the user enters 0.</p>
<p>Reasoning</p>
<ul>
<li><p>Does not matter how many numbers or times the user inputs</p>
</li>
<li><p>If it is 0, stop</p>
</li>
<li><p>Everything else continues</p>
</li>
</ul>
<p><strong>Code</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> n <span class="hljs-keyword">int</span>

fmt.Scan(&amp;n)

<span class="hljs-keyword">for</span> n != <span class="hljs-number">0</span> { <span class="hljs-comment">// if n is not 0, continue to print n, stop when n is 0</span>
        fmt.Println(n)
        fmt.Scan(&amp;n)
}
</code></pre>
<p><strong>Note:</strong> The loop condition is the stopping logic, not an if (which can also be used in this case). If the condition is false, the loop doesn’t run. I don’t need <code>break</code> if the condition already encodes the stop. This was a shift for me, and I got it.</p>
<ol>
<li><strong>Accumulator: compressing many values into one result.</strong></li>
</ol>
<p><strong>Thought process</strong></p>
<ul>
<li><p>Every input matters, but only keep a summary.</p>
</li>
<li><p>Sum, count, max, min, and average all are same family.</p>
</li>
<li><p>Initialize accumulator</p>
</li>
<li><p>Update it every iteration</p>
</li>
<li><p>Use comparison or addition</p>
</li>
</ul>
<p><strong>Example task  A: Sum</strong></p>
<p>Sum numbers until 0.</p>
<pre><code class="lang-go">sum := <span class="hljs-number">0</span>
<span class="hljs-keyword">var</span> n <span class="hljs-keyword">int</span>

fmt.Scan(&amp;n)

<span class="hljs-keyword">for</span> n != <span class="hljs-number">0</span> {
        sum += n
        fmt.Scan(&amp;n)
}
</code></pre>
<p><strong>Example task B: Max (this one confused me, rightly)</strong></p>
<p>Read numbers until 0 and find the max.</p>
<p>Reasoning</p>
<ul>
<li><p>No input is deleted or skipped</p>
</li>
<li><p>Every number is read/compared</p>
</li>
<li><p>I keep one running value</p>
</li>
<li><p>Sentinel loop, running comparison, store single value</p>
</li>
</ul>
<p><strong>Code</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> n <span class="hljs-keyword">int</span>

fmt.Scan(&amp;n)
max := n

<span class="hljs-keyword">for</span> n != <span class="hljs-number">0</span> {
        <span class="hljs-keyword">if</span> n &gt; max {
            max = n
        }
    fmt.Scan(&amp;n)
}
</code></pre>
<p><strong>Note:</strong> Finding max/min is not filtering, because I am not removing any input, I’m comparing every input against a stored state. It is a running comparison, a form of accumulation.</p>
<ol>
<li><strong>Filter: Some inputs don’t participate, different from accumulation.</strong></li>
</ol>
<p><strong>Thought process</strong></p>
<ul>
<li><p>Ignore some values.</p>
</li>
<li><p>If &amp; continue needed here.</p>
</li>
<li><p>Read input</p>
</li>
<li><p>Test condition</p>
</li>
<li><p>Skip or select</p>
</li>
</ul>
<p><strong>Example tasks</strong></p>
<ul>
<li><p>Ignore negatives</p>
</li>
<li><p>Skip multiples of 3</p>
</li>
<li><p>Count only evens</p>
</li>
<li><p>Ignore values in a range</p>
</li>
<li><p>Count even numbers until 0.</p>
</li>
</ul>
<p><strong>Code for (Count even numbers until 0)</strong></p>
<pre><code class="lang-go">count := <span class="hljs-number">0</span>
<span class="hljs-keyword">var</span> n <span class="hljs-keyword">int</span>

fmt.Scan(&amp;n)

<span class="hljs-keyword">for</span> n != <span class="hljs-number">0</span> {
        <span class="hljs-keyword">if</span> n % <span class="hljs-number">2</span> != <span class="hljs-number">0</span> {
                fmt.Scan(&amp;n)
                <span class="hljs-keyword">continue</span>
        }
        count++ (external counter)
        fmt.Scan(&amp;n)
}
</code></pre>
<p><strong>Note:</strong> Filtering means some inputs are discarded, which is different from max/min, where all inputs participate (accumulation).</p>
<ol>
<li><strong>Step Loop: Jumping with intention</strong></li>
</ol>
<p><strong>Thought process</strong></p>
<ul>
<li><p>The sequence itself has a pattern.</p>
</li>
<li><p>Loop + “if” can be used but cleaner is “controlled stepping”</p>
</li>
<li><p>No filtering needed</p>
</li>
<li><p>Skipping is needed</p>
</li>
</ul>
<p><strong>Example task</strong></p>
<ul>
<li><p>Even numbers</p>
</li>
<li><p>Countdown by 2s</p>
</li>
</ul>
<pre><code class="lang-go"><span class="hljs-comment">// Print multiples of 5 from 5 to 50.</span>
<span class="hljs-keyword">for</span> i := <span class="hljs-number">5</span>; i &lt;= <span class="hljs-number">50</span>; i += <span class="hljs-number">5</span> { <span class="hljs-comment">// skip/jump: “i += 5”</span>
        fmt.Println(i)
}
</code></pre>
<p><strong>Note:</strong> If the pattern is numeric, encode it in the step, not with if.</p>
<ol>
<li><strong>Shifting/Top-N: Keep the best few (this was stateful)</strong></li>
</ol>
<p><strong>Thought process</strong></p>
<ul>
<li><p>Keeping the ranked state is important</p>
</li>
<li><p>Best 3 values seen so far.</p>
</li>
<li><p>Compare</p>
</li>
<li><p>Shift values down (reassignment)</p>
</li>
<li><p>Insert at the top (preserve the order)</p>
</li>
</ul>
<p><strong>Example task</strong></p>
<p><strong>Concept (not code)</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Track top 3 numbers entered.</span>
<span class="hljs-built_in">new</span> &gt; first → shift (first → second → third)
<span class="hljs-built_in">new</span> &gt; second → shift
<span class="hljs-built_in">new</span> &gt; third → replace
</code></pre>
<p><strong>Note:</strong> This is controlled mutation. It builds on max/min, but extended with shifts &amp; slots</p>
<ol>
<li><strong>Nested Loops: Multiple dimensions (it clicked when I stopped thinking of them as “nested” but as outer loop &amp; inner loop)</strong></li>
</ol>
<p>Thought process</p>
<ul>
<li><p>The first loop is the row, another is the column.</p>
</li>
<li><p>Row loop defines context, time periods, column defines the relationship, it repeats within it</p>
</li>
<li><p>Outer loop = big picture</p>
</li>
<li><p>Inner loop = repeated detail</p>
</li>
</ul>
<p><strong>Example task</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Print a 4×4 grid. </span>
<span class="hljs-keyword">for</span> row := <span class="hljs-number">1</span>; row &lt;= <span class="hljs-number">4</span>; row++ {
        <span class="hljs-keyword">for</span> col := <span class="hljs-number">1</span>; col &lt;= <span class="hljs-number">4</span>; col++ {
                fmt.Print(<span class="hljs-string">"*"</span>)
        }
    fmt.Println()
}

<span class="hljs-comment">// Example 2: multiplication table.</span>
<span class="hljs-keyword">for</span> row := <span class="hljs-number">1</span>; row &lt;= <span class="hljs-number">10</span>; row++ {
    <span class="hljs-keyword">for</span> col := <span class="hljs-number">1</span>; col &lt;= <span class="hljs-number">10</span>; col++ {
        fmt.Printf(<span class="hljs-string">"%d x %d = %d\\n"</span>, row, col, row*col)
    }
}
</code></pre>
<p><strong>Note:</strong> Nested loops are not difficult, they’re just layered repetition. The inner loop <strong>completes</strong> before outer loop continues.</p>
<ol>
<li><strong>Map-Based Uniqueness: Something seen before, memory needed not repetition</strong></li>
</ol>
<p><strong>Thought process</strong></p>
<ul>
<li><p>Memory needed.</p>
</li>
<li><p>Map for tracking</p>
</li>
<li><p>Loop for reading</p>
</li>
<li><p>Conditional check</p>
</li>
<li><p>Past inputs matter</p>
</li>
<li><p>Uniqueness matters</p>
</li>
</ul>
<p><strong>Example task</strong></p>
<p>Count unique numbers entered.</p>
<pre><code class="lang-go">seen := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">int</span>]<span class="hljs-keyword">bool</span>)
<span class="hljs-keyword">var</span> n <span class="hljs-keyword">int</span>

fmt.Scan(&amp;n)
        <span class="hljs-keyword">for</span> n != <span class="hljs-number">0</span> {
                <span class="hljs-keyword">if</span> !seen[n] {
                seen[n] = <span class="hljs-literal">true</span>
        }
    fmt.Scan(&amp;n)
}
</code></pre>
<blockquote>
<p><strong>Producer vs Consumer Loops (this was a good realization, I picked up from learning this pattern)</strong> <em>The third loop in Go, “for-range”, is for consuming existing data. while-style for is for producing data.</em> <strong>Producer loop</strong> You are <strong>creating</strong> a new data from the input:</p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> input != <span class="hljs-string">"done"</span> {
    fmt.Scan(&amp;input)
}
</code></pre>
<p><strong>Consumer loop</strong> You are <strong>using (iterating through)</strong> an existing data:</p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> _, name := <span class="hljs-keyword">range</span> names {
    fmt.Println(name)
}
</code></pre>
</blockquote>
<p><strong>Note:</strong> When loops alone aren’t enough, data structures enter. The loop needs to remember what it has already seen, and the map keeps track.</p>
<hr />
<p>Now I understand that Control flow is not just about writing loops, it’s about recognizing <strong>repetition, decisions, state, and stopping conditions</strong>.</p>
<p>Before writing any <code>for</code> or <code>if</code> code, I pause and answer the following questions, to know the tools and concepts required.</p>
<ol>
<li><p>What kind of problem is this (Repetition? Decision? Accumulation? Selection? Ranking Memory?)</p>
</li>
<li><p>Am I repeating something, tracking something, or deciding something?</p>
</li>
<li><p>What state must I remember while the program runs?</p>
</li>
<li><p>What event or condition stops this process?</p>
</li>
</ol>
<p>Every control-flow problem fits into one or more of these.</p>
]]></content:encoded></item><item><title><![CDATA[Email 2FA: When My OTP Emails Refused to Send (Go + RabbitMQ + Elastic Email)]]></title><description><![CDATA[Context:
Building the Email 2FA (Two-Factor Authentication) feature for my backend.
Stack: Go, GORM, RabbitMQ, Elastic Email.
This is me, understanding how everything connects. This is not a tutorial, just real notes from debugging and clarity.
Start...]]></description><link>https://oyindanotes.hashnode.dev/go-rabbitmq-email-otp</link><guid isPermaLink="true">https://oyindanotes.hashnode.dev/go-rabbitmq-email-otp</guid><category><![CDATA[email2fa]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[rabbitmq]]></category><dc:creator><![CDATA[Oyindamola Abiola]]></dc:creator><pubDate>Thu, 30 Oct 2025 23:00:00 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-context"><strong>Context:</strong></h3>
<p>Building the Email 2FA (Two-Factor Authentication) feature for my backend.</p>
<p>Stack: Go, GORM, RabbitMQ, Elastic Email.</p>
<p>This is me, understanding how everything connects. This is not a tutorial, just real notes from debugging and clarity.</p>
<h3 id="heading-starting-point">Starting Point</h3>
<p>I wanted users to get an OTP (One-Time Password) via email for 2FA.</p>
<p>Simple idea: user requests → generate code → send email to user’s email address.</p>
<p>In reality, it went more like this:</p>
<pre><code class="lang-plaintext">User clicks "Send OTP" → Client
Backend generates OTP → Code logic
Producer - Publishes message to RabbitMQ → Queue
Consumer picks it up → Message picker
Delivers email via Elastic Email (SMTP)
</code></pre>
<p>And somewhere in that chain, something always went wrong.</p>
<h3 id="heading-error-1-relation-otpverifications-does-not-exist">Error 1: “relation ‘otp_verifications’ does not exist”</h3>
<p>This one was simple in hindsight:</p>
<p>GORM tried to insert into a table that didn’t exist yet.</p>
<p>I had created this OtpVerification model below, but didn’t migrate it to my database.</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> OtpVerification <span class="hljs-keyword">struct</span> {
    ID        uuid.UUID <span class="hljs-string">`gorm:"primarykey"`</span>
    UserID    uuid.UUID
    OtpCode   <span class="hljs-keyword">string</span>
    ExpiresAt time.Time
    Verified  <span class="hljs-keyword">bool</span>
    CreatedAt time.Time
}
</code></pre>
<h3 id="heading-the-fix">The Fix</h3>
<p>The fix was just to have it running in the database: <code>db.AutoMigrate(&amp;models.OtpVerification{})</code>.</p>
<p>Lesson: GORM doesn’t magically create tables; migration is my responsibility, geez!!.</p>
<h3 id="heading-error-2-unknown-task-type-from-emailotpqueue">Error 2: “Unknown task type from email_otp_queue”</h3>
<p>I am using <strong>RabbitMQ</strong> to send tasks to a background worker.</p>
<p>I wanted to publish a message to a queue and have another process handle the actual email sending.</p>
<p>Producer = the Go service that <strong>publishes</strong> the task in connection with the RabbitMQ (queue)</p>
<p>Consumer = the background worker that <strong>reads</strong> and <strong>processes</strong> the task for delivery.</p>
<p>My mistake: when I published the OTP message, I didn’t add targeted user’s email address and specify the type properly.</p>
<p>Here’s the code that was broken:</p>
<pre><code class="lang-go">utils.PublishMessage(
    os.Getenv(<span class="hljs-string">"EMAIL_OTP_QUEUE"</span>),
    otp.UserID.String(), <span class="hljs-comment">// userID instead of email</span>
    generateOtp,
    <span class="hljs-string">""</span>,                  <span class="hljs-comment">// empty description</span>
    <span class="hljs-string">""</span>,
    user.Email,
    <span class="hljs-string">""</span>,
)
</code></pre>
<p>The consumer was expecting a <code>"Type": "otp"</code> message to know which handler to call.</p>
<p>But I sent an empty string. So RabbitMQ successfully added it to the queue, but the consumer didn’t know what to do because of <em>“unknown task type.”</em> This mistake was an oversight; I copied the code block from a different feature and forgot to modify it.</p>
<h3 id="heading-the-fix-1">The Fix</h3>
<p>After understanding what was happening, I updated it:</p>
<pre><code class="lang-go">utils.PublishMessage(
    os.Getenv(<span class="hljs-string">"EMAIL_OTP_QUEUE"</span>),
    user.Email,       <span class="hljs-comment">// email</span>
    generateOtp,
    <span class="hljs-string">"otp"</span>,            <span class="hljs-comment">// OTP</span>
    <span class="hljs-string">""</span>,
    <span class="hljs-string">""</span>,
    <span class="hljs-string">""</span>,
)
</code></pre>
<p>Now the logs looked like this:</p>
<pre><code class="lang-plaintext">Published task to email_otp_queue for oyi.....@gmail.com
Sent otp email to oyi.....@gmail.com
</code></pre>
<p>Finally sent the OTP to my inbox, and yeah, I got it.</p>
<p>Lesson:</p>
<ul>
<li>Always review and verify copied code blocks</li>
</ul>
<h3 id="heading-error-3-535-authentication-failed-access-denied">Error 3: “535 Authentication failed: Access denied”</h3>
<p>This one was from <strong>Elastic Email (deliverer)</strong>. I had changed my account email to <code>oyi.....@</code><a target="_blank" href="http://gmail.com"><code>gmail.com</code></a>, but my SMTP username was still the old <code>aod.....@</code><a target="_blank" href="http://gmail.com"><code>gmail.com</code></a>. Elastic Email SMTP credentials are <strong>account-level</strong>, not tied to the sender identity only.</p>
<p>Even though I verified the new email, the actual SMTP authentication was still using the old one.</p>
<p>The dashboard said:</p>
<blockquote>
<p>“For testing purposes, you can only send to your registered email address.”</p>
</blockquote>
<p>I knew about Elastic Email restrictions on free accounts to send only to the verified sender address for testing, which I had done.</p>
<h3 id="heading-the-fix-2">The Fix</h3>
<p>I created a new SMTP using the new, active (verified) account, deleted the old one, and it worked, I got the OTP email.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762175784242/6985a755-e10d-490a-ac23-f2fcb28abd8b.png" alt class="image--center mx-auto" /></p>
<p><strong>Lessons:</strong></p>
<ul>
<li><p>You can verify multiple “from” addresses on Elastic Email</p>
</li>
<li><p>But your SMTP username remains the one you registered with (main issue)</p>
</li>
<li><p>If you change your login email, you may need a new API key</p>
</li>
</ul>
<h3 id="heading-how-it-all-connects-my-mental-model-now">How It All Connects (my mental model now)</h3>
<pre><code class="lang-plaintext">User → /request-otp → Service layer → PublishMessage()
       → RabbitMQ queue → EmailConsumer → SendMail() → Elastic Email → Inbox
</code></pre>
<p>Each layer has its responsibility:</p>
<ul>
<li><p><strong>Service layer -</strong> business logic (create OTP, store it in the db)</p>
</li>
<li><p><strong>Publisher -</strong> posts a task to RabbitMQ i.e queue</p>
</li>
<li><p><strong>Consumer -</strong> listens, reads the message, and executes email sending</p>
</li>
<li><p><strong>Elastic Email (SMTP) -</strong> actually delivers the email to the user</p>
</li>
</ul>
<p>RabbitMQ is just the middleman, like a post office between the app and the worker that sends the mail. Both the publisher (producer) and consumer are connected to the RabbitMQ</p>
<h3 id="heading-key-lessons">Key Lessons</h3>
<ol>
<li><p>Every queue message must match what the consumer expects, <em>fields, types, and purpose.</em></p>
</li>
<li><p>SMTP credentials are tied to your sending account, not your email variable.</p>
</li>
<li><p>RabbitMQ doesn’t execute, it only <em>transfers</em>.</p>
</li>
<li><p>Always test both ends: producer and consumer.</p>
</li>
<li><p>Logs are your ally, Oyindamola, read them slowly.</p>
</li>
<li><p>The real “fix” is understanding the flow, not just changing code.</p>
</li>
</ol>
<p><strong>Clarity: T</strong>he architecture makes sense now. It taught me how background queues actually work, how they all connect to achieve the end goal.</p>
]]></content:encoded></item></channel></rss>