<?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[Alex Talks Tech – Real-World Cloud, Software Engineering Insights]]></title><description><![CDATA[Developer journal from Alex Nyambura, covering software, cloud infrastructure, and startup lessons. Real setups, raw configs, and honest takes from the trenches.]]></description><link>https://blog.lxmwaniky.me</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 08:51:36 GMT</lastBuildDate><atom:link href="https://blog.lxmwaniky.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[I Scored 100% on a Safaricom Codility Test. The Practical Part Was Elasticsearch. Let's Talk About It.]]></title><description><![CDATA[I'm a cloud engineer. My 2026 goal is to land an internship at a Big Tech company. That's what I've been building toward. Upskilling in tech, grinding DSA, the whole thing.
Applying for an attachment ]]></description><link>https://blog.lxmwaniky.me/elasticsearch</link><guid isPermaLink="true">https://blog.lxmwaniky.me/elasticsearch</guid><category><![CDATA[kibana]]></category><category><![CDATA[elasticsearch]]></category><category><![CDATA[codility]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 22 Mar 2026 15:03:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6886a57d8cf7fa7ec87a9bfd/f449304b-30f9-45a3-9d26-9627eb6c047c.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I'm a cloud engineer. My 2026 goal is to land an internship at a Big Tech company. That's what I've been building toward. Upskilling in tech, grinding DSA, the whole thing.</p>
<p>Applying for an attachment at <a href="https://www.linkedin.com/company/safaricom">Safaricom</a> was almost a side quest. I was so nervous going in, with imposter syndrome (I have it even more rn as I'm waiting for the next steps). I had been grinding arrays, trees, and dynamic programming for weeks. None of that came.</p>
<p>There were 2 tasks, and task 2 was about the "Elasticsearch Search API."</p>
<p>I paused. Then I smiled. Elasticsearch had been on my learning track for a while. Funny how that works.</p>
<p>All tests passed. 100%.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6886a57d8cf7fa7ec87a9bfd/e3daa3cc-e126-481d-b99e-c2ba141d5e12.png" alt="" style="display:block;margin:0 auto" />

<p>But this post isn't really about the test. It's about Elasticsearch. What it is, how it actually works, and why I think every developer building anything at scale should understand it.</p>
<hr />
<h2>What is Elasticsearch?</h2>
<p>Elasticsearch is a distributed, open-source search and analytics engine built on top of <strong>Apache Lucene</strong>. It stores data as JSON documents and lets you search, analyze, and visualize it at scale — in near real-time.</p>
<p>It's the <strong>E</strong> in the <strong>ELK Stack</strong> (Elasticsearch, Logstash, Kibana).</p>
<h2>Why Does Elasticsearch Even Exist?</h2>
<p>Here's the problem it solves.</p>
<p>You have a blog. Thousands of posts. A user types "nairobi tech" in your search bar. The naive solution:</p>
<pre><code class="language-sql">SELECT * FROM posts WHERE body LIKE '%nairobi%' OR body LIKE '%tech%';
</code></pre>
<p>That works. Until you have 10 million posts. Then it crawls. Then it times out. Then your users leave and never come back.</p>
<p>The issue isn't hardware. It's the <em>approach</em>. A relational database is optimised for retrieving exact records. It's not built for finding everything relevant to a phrase across millions of rows.</p>
<p>Elasticsearch solves this with an <strong>inverted index</strong>.</p>
<p>Think of the index at the back of a textbook. Instead of reading every page to find where "Nairobi" is mentioned, you flip to the index, and it tells you exactly which pages. Elasticsearch builds that index on your data automatically, keeps it updated in real time, and when you search, it doesn't scan. It looks up.</p>
<p>That's why it returns results in milliseconds even at massive scale.</p>
<hr />
<h2>The Inverted Index, Visualised</h2>
<p>Here's what happens when you index three blog posts:</p>
<table>
<thead>
<tr>
<th>Word</th>
<th>Documents</th>
</tr>
</thead>
<tbody><tr>
<td>nairobi</td>
<td>Post 1, Post 2</td>
</tr>
<tr>
<td>tech</td>
<td>Post 1, Post 3</td>
</tr>
<tr>
<td>startups</td>
<td>Post 2</td>
</tr>
<tr>
<td>booming</td>
<td>Post 1</td>
</tr>
<tr>
<td>mombasa</td>
<td>Post 3</td>
</tr>
</tbody></table>
<p>Search "nairobi tech" → hit the index → instantly know Post 1 and Post 2 are relevant. No scanning. No guessing. That's the entire trick.</p>
<hr />
<h2>Elasticsearch vs SQL</h2>
<p>Before touching any code, lock this in:</p>
<table>
<thead>
<tr>
<th>Elasticsearch</th>
<th>SQL</th>
<th>In context</th>
</tr>
</thead>
<tbody><tr>
<td>Index</td>
<td>Table</td>
<td><code>blog_posts</code></td>
</tr>
<tr>
<td>Document</td>
<td>Row</td>
<td>One blog post</td>
</tr>
<tr>
<td>Field</td>
<td>Column</td>
<td><code>title</code>, <code>body</code>, <code>author</code></td>
</tr>
<tr>
<td>Mapping</td>
<td>Schema</td>
<td>Field type definitions</td>
</tr>
<tr>
<td>Query</td>
<td>SELECT</td>
<td>What you ask</td>
</tr>
<tr>
<td>Shard</td>
<td>Partition</td>
<td>A chunk of the index</td>
</tr>
</tbody></table>
<p>The key difference: in SQL, you retrieve. In Elasticsearch, you search and rank. Results come back with a relevance score. Not just found or not found, but how well does this match?</p>
<hr />
<h2>Setting Up Locally</h2>
<p>Fastest way to get running, one command:</p>
<pre><code class="language-shell">curl -fsSL https://elastic.co/start-local | sh
</code></pre>
<p>This spins up Elasticsearch on port <code>9200</code> and Kibana on port <code>5601</code>. Kibana is the UI that sits on top. Browse your data, build dashboards, and run queries visually. Start with raw HTTP requests first. Understanding what's happening underneath makes Kibana make sense.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6886a57d8cf7fa7ec87a9bfd/7f5985fa-168e-4309-b17a-29da9bc127b6.png" alt="" style="display:block;margin:0 auto" />

<p>Verify it's alive: (I used Postman)</p>
<pre><code class="language-shell">GET http://localhost:9200
</code></pre>
<p>You'll get back cluster info. Remember to authenticate with your basic creds or API Key provided during set-up</p>
<img src="https://cdn.hashnode.com/uploads/covers/6886a57d8cf7fa7ec87a9bfd/8c373ced-c7c4-44a2-9ad5-af990c4d1a9c.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>Creating an Index (and Why Mappings Matter)</h2>
<p>Creating an index is like defining a schema, except you're also telling Elasticsearch <em>how</em> to treat each field. This is the most important decision you'll make.</p>
<pre><code class="language-json">PUT /blog_posts
{
  "mappings": {
    "properties": {
      "title":        { "type": "text" },
      "body":         { "type": "text" },
      "author":       { "type": "keyword" },
      "tags":         { "type": "keyword" },
      "published_at": { "type": "date" },
      "views":        { "type": "integer" }
    }
  }
}
</code></pre>
<p>Two field types are doing completely different jobs:</p>
<p><code>text</code> gets analysed before indexing. "Building Tech Communities in Nairobi" becomes <code>["building", "tech", "communities", "nairobi"]</code>. This powers natural language search.</p>
<p><code>keyword</code> is stored exactly as-is. No analysis. Used for exact matching, filtering, sorting, and aggregations.</p>
<p>If you map <code>author</code> as <code>text</code> when you meant <code>keyword</code>, filtering by exact author name won't work cleanly. And you can't change field types on an existing index without reindexing everything. <strong>Map it right the first time.</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/6886a57d8cf7fa7ec87a9bfd/63d24421-954b-43db-a0d4-f93b337d3f7a.png" alt="" style="display:block;margin:0 auto" />

<hr />
<p>Now insert some documents to play with. Each <code>POST</code> to <code>_doc</code> creates one document with an auto-generated ID:</p>
<pre><code class="language-json">POST /blog_posts/_doc
{
  "title": "Building tech communities in Nairobi",
  "body": "The Nairobi tech ecosystem has grown significantly. Developer communities like GDG are driving this growth through events and mentorship.",
  "author": "Alex Nyambura",
  "tags": ["community", "nairobi", "devrel"],
  "published_at": "2025-03-01",
  "views": 340
}
</code></pre>
<pre><code class="language-json">POST /blog_posts/_doc
{
  "title": "Getting started with Kubernetes on GKE",
  "body": "Google Kubernetes Engine makes deploying containerized apps straightforward. This guide walks through setting up your first cluster.",
  "author": "Alex Nyambura",
  "tags": ["devops", "kubernetes", "gcp"],
  "published_at": "2025-04-15",
  "views": 820
}
</code></pre>
<pre><code class="language-json">POST /blog_posts/_doc
{
  "title": "ISP infrastructure in rural Kenya",
  "body": "Last-mile internet connectivity remains a challenge. Companies like Wakanet are bridging the gap in underserved regions.",
  "author": "Felix Jumason",
  "tags": ["infrastructure", "kenya", "internet"],
  "published_at": "2025-05-10",
  "views": 210
}
</code></pre>
<p>Confirm they're in:</p>
<pre><code class="language-plaintext">GET /blog_posts/_count
</code></pre>
<p>You should see <code>"count": 3</code>. Now you have something real to search against.</p>
<hr />
<h2>Searching: The Three Patterns You'll Use Most</h2>
<p><strong>1. Full-text search with</strong> <code>match</code><strong>:</strong></p>
<pre><code class="language-json">GET /blog_posts/_search
{
  "query": {
    "match": {
      "body": "nairobi tech community"
    }
  }
}
</code></pre>
<p><code>match</code> tokenizes your query the same way it tokenized the document at index time, then scores matches using BM25. Results come back ranked by relevance, highest first.</p>
<p><strong>2. Search across multiple fields with</strong> <code>multi_match</code><strong>:</strong></p>
<pre><code class="language-json">GET /blog_posts/_search
{
  "query": {
    "multi_match": {
      "query": "kenya",
      "fields": ["title", "body", "tags"]
    }
  }
}
</code></pre>
<p>Real users don't know which field their keyword lives in. <code>multi_match</code> is what you actually want behind a search bar.</p>
<p><strong>3. Combining conditions with</strong> <code>bool</code><strong>:</strong></p>
<pre><code class="language-json">GET /blog_posts/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "body": "kubernetes" } }
      ],
      "filter": [
        { "term": { "author": "Alex Nyambura" } },
        { "range": { "views": { "gte": 500 } } }
      ]
    }
  }
}
</code></pre>
<p>This is the pattern you'll use 80% of the time in production. <code>must</code> is for relevance and affects the score. <code>filter</code> is for hard conditions that don't affect scoring and is faster because Elasticsearch caches filters.</p>
<blockquote>
<p><strong>relevance goes in</strong> <code>must</code><strong>, hard conditions go in</strong> <code>filter</code><strong>.</strong></p>
</blockquote>
<hr />
<h2>Understanding Scores (BM25 in Plain English)</h2>
<p>Every result has a <code>_score</code>. Three things push it up:</p>
<p><strong>Term frequency:</strong> "Nairobi" appears 5 times in document A, once in document B. A wins.</p>
<p><strong>Inverse document frequency:</strong> "Nairobi" only appears in 2 of 1000 documents, so it's a strong signal. "The" appears in all 1000, so searching for it tells you nothing. Common words carry low weight automatically.</p>
<p><strong>Field length:</strong> "Primo Levi" in a 2-word author field is a stronger match than "Primo Levi" buried in a 500-word body.</p>
<p>Scores are only meaningful relative to each other within the same query. There's no fixed scale. A score of <code>1.9</code> in one query means nothing compared to <code>1.9</code> in a different query.</p>
<hr />
<h2>Pagination, Sorting, and Field Filtering</h2>
<p><strong>Return only the first 2 results:</strong></p>
<pre><code class="language-json">{
  "size": 2,
  "from": 0,
  "query": { "match_all": {} }
}
</code></pre>
<p><code>size</code> = how many to return. <code>from</code> = offset. Page 2 would be <code>"from": 2</code>. The total hit count always reflects the full match count, not just what's shown on the current page.</p>
<p><strong>Sort by year descending:</strong></p>
<pre><code class="language-json">{
  "sort": [{ "year": { "order": "desc" } }],
  "query": { "match_all": {} }
}
</code></pre>
<p>When you sort by a field, <code>_score</code> becomes <code>null</code>. There's no relevance calculation. You're ordering, not ranking.</p>
<p><strong>Return only specific fields:</strong></p>
<pre><code class="language-json">{
  "_source": ["author", "title"],
  "query": { "match_all": {} }
}
</code></pre>
<blockquote>
<p>Never return entire documents when you only need two fields. Especially at scale.</p>
</blockquote>
<hr />
<h2>What's Next From Here</h2>
<p>If this sparked something:</p>
<ul>
<li><p><strong>Fuzzy search:</strong> <code>"fuzziness": "AUTO"</code> and Elasticsearch handles typos automatically</p>
</li>
<li><p><strong>Aggregations:</strong> the analytics side. "Top 5 authors by post count", "views by month". SQL's <code>GROUP BY</code> but distributed and fast</p>
</li>
<li><p><strong>Analyzers:</strong> customise how text gets tokenized. Build autocomplete, handle edge cases</p>
</li>
<li><p><strong>Kibana:</strong> the UI that ships with Elasticsearch. Great for exploring data and visualising aggregations once you understand the raw query layer</p>
</li>
</ul>
<hr />
<h2>One More Thing</h2>
<p>I'm still in the middle of the Safaricom internship application process. There's more to that story and I'll write the full thing once it's wrapped up. The application process, what the assessment was actually like, what I'd tell someone going in cold.</p>
<p>For now, Elasticsearch isn't magic. It's a really smart index with a clean query API. Once that clicks, you'll start seeing use cases for it everywhere.</p>
<p>And if you're preparing for a technical assessment at any company that deals with large amounts of data, add it to your list. It came up for me. It might come up for you.</p>
<hr />
<p><em>Let's connect on</em> <a href="https://linkedin.com/in/lxmwaniky">LinkedIn</a> <em>or check out more posts at</em> <a href="https://blog.lxmwaniky.me"><em>blog.lxmwaniky.me</em></a><em>. I write about cloud, Software, and building things in the Kenyan tech ecosystem.</em></p>
]]></content:encoded></item><item><title><![CDATA[Aced the ACE: A Strategic Guide to the Google Cloud Associate Cloud Engineer Exam]]></title><description><![CDATA[I apologize this took so long to get out. Between starting a new role as a Software Engineer and balancing the 2:00 AM grind, I had to wait until I could sit down and write something that actually adds value. But I’m coming to you now with the missio...]]></description><link>https://blog.lxmwaniky.me/aced-the-ace</link><guid isPermaLink="true">https://blog.lxmwaniky.me/aced-the-ace</guid><category><![CDATA[Associate Cloud Engineer]]></category><category><![CDATA[GCP Certification,]]></category><category><![CDATA[google cloud]]></category><category><![CDATA[Certification]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 08 Feb 2026 10:22:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770545011860/7318688d-19e7-4ac8-86ee-7c915a991db2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I apologize this took so long to get out. Between starting a new role as a Software Engineer and balancing the 2:00 AM grind, I had to wait until I could sit down and write something that actually adds value. But I’m coming to you now with the mission accomplished: I’m officially <a target="_blank" href="https://www.credly.com/users/lxmwaniky/"><strong>3x Google Cloud Certified</strong></a>, having secured the "Triple Crown"—Foundational, Associate (ACE), and Professional (PMLE).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770545144236/4f1deaf2-f60f-40d8-8436-58e557852bb4.png" alt class="image--center mx-auto" /></p>
<p>If you missed the deep dives on the other two, check out my guides on <a target="_blank" href="https://www.google.com/url?sa=E&amp;q=https%3A%2F%2Fblog.lxmwaniky.me%2Fgen-ai-leader">How to Pass the Generative AI Leader Certification</a> and the heavy-duty <a target="_blank" href="https://www.google.com/url?sa=E&amp;q=https%3A%2F%2Fblog.lxmwaniky.me%2Fpmle">Professional Machine Learning Engineer (PMLE) audit</a>.</p>
<p>Today, we go under the hood of the <a target="_blank" href="https://cloud.google.com/learn/certification/cloud-engineer/"><strong>Associate Cloud Engineer (ACE)</strong></a>.</p>
<p>The ACE is the "Mechanic’s Exam." It doesn't care about your high-level strategy or your AI vision. It cares if you know the exact gcloud command to resize a cluster or which specific role prevents a billing disaster. It is an audit of your operational intuition. Here is the detailed blueprint of how I cleared it.</p>
<hr />
<h3 id="heading-1-the-core-pillar-obsessing-over-the-hierarchy">1. The Core Pillar: Obsessing Over the "Hierarchy"</h3>
<p>In the ACE exam, Google tests your ability to act as a gatekeeper. If you don't understand the <a target="_blank" href="https://docs.cloud.google.com/iam/docs"><strong>Resource Hierarchy</strong></a>, you’ll get lost in the logic puzzles.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770545317741/cfaf213a-97f7-4ed5-99ca-92da5c04deba.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><strong>The Blueprint:</strong> You have to visualize the cloud like a massive construction project.</p>
<ul>
<li><p><strong>Organization:</strong> The County Government (the domain).</p>
</li>
<li><p><strong>Folders:</strong> The Estates (grouping projects by department or environment like Dev/Prod).</p>
</li>
<li><p><strong>Projects:</strong> The specific Plot of Land. This is the base unit. <strong>Billing lives here.</strong></p>
</li>
<li><p><strong>Resources:</strong> The actual house (VMs, Buckets, GKE clusters).</p>
</li>
</ul>
</li>
<li><p><strong>The Law of Inheritance:</strong> Policies flow <strong>down</strong>. You can never "deny" at a lower level what was "allowed" at a higher level. Google uses "Union" logic—if any level gives you access, you have it.</p>
</li>
<li><p><a target="_blank" href="https://docs.cloud.google.com/iam/docs/using-iam-securely#least_privilege"><strong>Least Privilege is the Law</strong></a><strong>:</strong> If a question asks how to give a developer access, and "Owner" is an option, it’s probably a distractor. Google wants you to pick a <strong>Predefined Role</strong> (like Compute Instance Admin). Avoid <strong>Primitive Roles</strong> at all costs.</p>
</li>
</ul>
<h3 id="heading-2-the-mechanics-toolset-cli-vs-console">2. The "Mechanic’s" Toolset: CLI vs. Console</h3>
<p>You cannot pass this exam by just clicking buttons in the UI. You need to understand the <a target="_blank" href="https://cloud.google.com/sdk"><strong>Cloud SDK</strong></a>.</p>
<ul>
<li><p><strong>The Syntax Trap:</strong> One of the easiest ways to spot a wrong answer is the command prefix.</p>
<ul>
<li><p><a target="_blank" href="https://docs.cloud.google.com/sdk/docs/cheatsheet">gcloud</a> is for almost everything (VMs, IAM, Projects).</p>
</li>
<li><p><a target="_blank" href="https://docs.cloud.google.com/storage/docs/gsutil">gsutil</a> is strictly for <strong>Cloud Storage</strong> (Buckets/Objects).</p>
</li>
<li><p><a target="_blank" href="https://docs.cloud.google.com/bigquery/docs/quickstarts/load-data-bq">bq</a> is strictly for <strong>BigQuery</strong>.</p>
</li>
<li><p><a target="_blank" href="https://docs.cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl">kubectl</a> is for <strong>GKE</strong>.</p>
</li>
</ul>
</li>
<li><p><strong>Scenario Logic:</strong> If an answer suggests using gsutil to create a VM, you can ignore it in one second. I spent my night shifts typing these out in the Cloud Shell to make the syntax second nature.</p>
</li>
</ul>
<h3 id="heading-3-networking-the-final-boss-of-the-ace">3. Networking: The "Final Boss" of the ACE</h3>
<p><img src="https://docs.cloud.google.com/static/vpc/images/vpc-overview-example.svg" alt="VPC network example." /></p>
<p><a target="_blank" href="https://docs.cloud.google.com/vpc/docs/vpc">Networking</a> is where most candidates fail. Google’s networking logic is unique, and the exam exploits that.</p>
<ul>
<li><p><strong>VPC is Global:</strong> This is a major point of confusion for those coming from AWS. Your VPC spans the world.</p>
</li>
<li><p><a target="_blank" href="https://docs.cloud.google.com/vpc/docs/vpc#vpc_networks_and_subnets"><strong>Subnets</strong></a> <strong>are Regional:</strong> You don't have global subnets. You have a subnet in us-central1 and another in europe-west1.</p>
</li>
<li><p><a target="_blank" href="https://docs.cloud.google.com/load-balancing/docs/load-balancing-overview"><strong>Load Balancing</strong></a> <strong>Tiers:</strong></p>
<ul>
<li><p><strong>Global (Layer 7):</strong> HTTP(S) Load Balancing. Use this if you need to route based on the URL path (e.g., /api goes to a different backend than /images).</p>
</li>
<li><p><strong>Regional (Layer 4):</strong> Network Load Balancing. Use this for high-performance TCP/UDP traffic where you don't care about the application content.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-4-the-audit-strategy-200-questions-and-keyword-hunting">4. The "Audit" Strategy: 200 Questions and Keyword Hunting</h3>
<p>Consistency beats intensity. I ran through <strong>over 200 practice questions</strong> to train my brain to find the "Anchor Word" in every scenario.</p>
<ul>
<li><p><strong>"Cost-Effective":</strong> Look for <strong>Spot/Preemptible VMs</strong> or <strong>Archive Storage</strong>.</p>
</li>
<li><p><strong>"Minimal Maintenance":</strong> Stop looking at GCE (VMs) and start looking at <strong>Cloud Run</strong> or <strong>App Engine</strong>.</p>
</li>
<li><p><strong>"Hybrid":</strong> This is a signal for <strong>Cloud VPN</strong> (cheap/internet) vs. <strong>Cloud Interconnect</strong> (expensive/direct fiber).</p>
</li>
</ul>
<h3 id="heading-5-the-ai-proctor-using-llms-for-gap-analysis">5. The AI Proctor: Using LLMs for Gap Analysis</h3>
<p>I used Gemini and Claude as private tutors to find my "Logical Gaps." I didn't just ask them for answers; I asked them to simulate the pressure. Here is the exact prompt I used to turn an AI into a Google Interviewer:</p>
<blockquote>
<p><strong>The ACE Simulator Prompt:</strong><br /><em>"Act as a Google Cloud Associate Cloud Engineer (ACE) exam simulator. Generate a full-length mock exam with 50 multiple-choice questions matching the real ACE exam difficulty and syllabus. Each question must have 4 options and only one correct answer. Mix scenario-based questions (e.g., 'A company needs to...') with direct technical questions. Cover IAM, Compute, Storage, Networking, GKE, and Monitoring. After I submit, show my score and provide a 'Senior Engineer Review' explaining why the correct answer is Google's Best Practice and why the other three are 'Toil' or security risks."</em></p>
</blockquote>
<h3 id="heading-6-the-essential-resource-stack">6. The Essential Resource Stack</h3>
<p>I cut the noise and stuck to these high-signal resources:</p>
<ol>
<li><strong>The Foundation:</strong> <a target="_blank" href="https://youtu.be/jpno8FSqpc8?si=mcpng6GNSrnNTTx5">Antoni Tzavelas’ video on freeCodeCamp</a>. It’s 20 hours of pure operational gold. It’s the only resource that actually explains the "Why" behind the commands.</li>
</ol>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=jpno8FSqpc8">https://www.youtube.com/watch?v=jpno8FSqpc8</a></div>
<p> </p>
<ol>
<li><p><strong>The Manual:</strong> <a target="_blank" href="https://www.cloudskillsboost.google/paths/11"><strong>Official Google Cloud Skills Boost</strong></a>. I did every lab until I could do them without looking at the instructions.</p>
</li>
<li><p><strong>The Validation:</strong> <a target="_blank" href="https://docs.google.com/forms/d/e/1FAIpQLSfexWKtXT2OSFJ-obA4iT3GmzgiOCGvjrT9OfxilWC1yPtmfQ/viewform">Google's Sample Exam</a>. Google Sample Practice exams are notoriously harder than the real thing. If you can hit 85%, you will walk through the actual exam.</p>
</li>
<li><p><strong>The Cheat Sheets:</strong> <a target="_blank" href="https://www.google.com/url?sa=E&amp;q=https%3A%2F%2Flearngood.com">Learngood.com</a> for quick domain summaries when I had 10 minutes between university errands.</p>
</li>
</ol>
<hr />
<h3 id="heading-final-logic-automate-or-die">🏁 Final Logic: Automate or Die</h3>
<p>The ACE exam is Google’s way of asking: <em>"Can we trust you with our hardware?"</em></p>
<p>Google's engineering culture is built on eliminating <strong>Toil</strong> (manual, repetitive work). Whenever you are stuck between two answers that both work, <strong>choose the one that requires the least human intervention.</strong></p>
<p>You’ve got the roadmap. Now, go put in the night shifts. The view from the top of the 3x mountain is worth the climb.</p>
<blockquote>
<p><em>Co-written with Google</em> <a target="_blank" href="https://gemini.google.com/"><em>Gemini</em></a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Terraform Explained: Why Your Cloud Infrastructure Needs a Blueprint]]></title><description><![CDATA[Let’s be real for a second.
We’ve all been there. You start a new project on Google Cloud or AWS. You’re excited. You log into the console, click "Create Instance," pick a region (probably europe-west1 because latency to Nairobi matters), open a few ...]]></description><link>https://blog.lxmwaniky.me/terraform-explained</link><guid isPermaLink="true">https://blog.lxmwaniky.me/terraform-explained</guid><category><![CDATA[Terraform]]></category><category><![CDATA[GCP]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Thu, 08 Jan 2026 04:39:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767846055902/c692b02d-eb0b-4e8a-aaa4-4d7175271fb7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Let’s be real for a second.</strong></p>
<p>We’ve all been there. You start a new project on <a target="_blank" href="https://console.cloud.google.com/">Google Cloud</a> or AWS. You’re excited. You log into the console, click "Create Instance," pick a region (probably europe-west1 because latency to Nairobi matters), open a few ports on the firewall, and your app is live. You feel like a genius.</p>
<p>But then, three months later, you need to replicate that environment for staging. Or worse, you accidentally delete a firewall rule and take down production. You try to remember what settings you clicked. Was it n1-standard-1 or e2-micro? Did I allow port 8080 or just 80?</p>
<p>Suddenly, you’re not a Cloud Engineer anymore. You’re just a guy trying to remember where he put the keys.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767846456623/7b8b54e6-684c-4c37-9cec-2c9014c659d0.png" alt="Image Generated by Gemini" class="image--center mx-auto" /></p>
<p>This is the "Jua Kali" approach to the cloud. It works until it doesn't.</p>
<p>In the professional world, we don't click buttons. We write code. We use <a target="_blank" href="https://developer.hashicorp.com/terraform/docs"><strong>Terraform</strong></a>. And today, I’m going to teach you exactly what that is using the universal language of Mjengo (Construction).</p>
<h2 id="heading-the-problem-the-freestyling-fundi">The Problem: The "Freestyling Fundi"</h2>
<p>Imagine you bought a plot in Ruiru and you want to build a house. You hire a local Fundi. You go to the site, point at the ground, and say, "Weka ukuta hapa" (put a wall here).</p>
<p>The next day, you come back. The wall is there, but it’s crooked. The kitchen is where the bathroom should be. If you wanted to build an <em>identical</em> house next door, you couldn't. You’d have to stand there and shout instructions all over again. There is no documentation. There is no version control. It’s just vibes.</p>
<p><strong>This is what happens when you manage cloud infrastructure manually via the Console.</strong> It’s prone to human error, it’s not reproducible, and it’s a nightmare to scale.</p>
<h2 id="heading-the-solution-the-architects-blueprint-iac">The Solution: The Architect’s Blueprint (IaC)</h2>
<p>Now, imagine the professional way. You hire an Architect. They don't touch a single brick. Instead, they produce a <strong>Blueprint</strong>.</p>
<p>This blueprint defines <em>everything</em>. The dimensions, the materials, the plumbing, the electrical wiring. You can hand this blueprint to <em>any</em> construction crew, and they will build the exact same house. Whether it's in Ruiru, Mombasa, or Kisumu—if the blueprint is the same, the house is the same.</p>
<p><strong>This is</strong> <a target="_blank" href="https://en.wikipedia.org/wiki/Infrastructure_as_code"><strong>Infrastructure as Code</strong></a> <strong>(IaC).</strong> And <strong>Terraform</strong> is the tool we use to write the blueprint.</p>
<h2 id="heading-core-concepts">Core Concepts</h2>
<p>Terraform might look intimidating with its .tf files and HashiCorp Configuration Language (HCL), but it maps perfectly to construction concepts. Let's break it down.</p>
<h3 id="heading-1-the-provider-the-contractor">1. The Provider (The Contractor)</h3>
<p>Before you break ground, you need to know <em>who</em> is doing the work. Are you hiring a crew that knows how to build on Google Cloud (GCP)? Or are you hiring a crew for AWS?</p>
<p>In Terraform, this is the <strong>Provider</strong>.</p>
<pre><code class="lang-plaintext">    # provider.tf
provider "google" {
  project = "my-awesome-project"
  region  = "us-central1"
}
</code></pre>
<p>This block tells Terraform: <em>"Hey, we are hiring the Google Cloud crew. All instructions following this are for them."</em></p>
<h3 id="heading-2-resources-the-building-blocks">2. Resources (The Building Blocks)</h3>
<p>This is the meat of the project. A resource is a specific thing you want to build. In a mjengo, it's a wall, a window, or a water tank. In the cloud, it's a Virtual Machine, a Storage Bucket, or a Database.</p>
<p>For example, creating a bucket to store images:</p>
<pre><code class="lang-plaintext">    # main.tf
resource "google_storage_bucket" "image_store" {
  name     = "my-app-images-bucket"
  location = "US"
}
</code></pre>
<ul>
<li><p>resource: The keyword that screams "I want to build something!"</p>
</li>
<li><p>google_storage_bucket: The <em>type</em> of material we are using.</p>
</li>
<li><p>image_store: The internal nickname we give it (like calling a room "Master Bedroom").</p>
</li>
</ul>
<h3 id="heading-3-variables-customizing-the-house">3. Variables (Customizing the House)</h3>
<p>Imagine you have a perfect blueprint for a 3-bedroom house. Client A wants the walls painted blue. Client B wants them white. You don't draw a whole new blueprint. You just change the "Paint Specification."</p>
<p><strong>Variables</strong> allow you to make your code reusable.</p>
<pre><code class="lang-plaintext">    # variables.tf
variable "environment" {
  description = "Are we in dev or prod?"
  type        = string
}

# main.tf
resource "google_compute_instance" "server" {
  name         = "app-server-${var.environment}"
  machine_type = var.environment == "prod" ? "e2-standard-4" : "e2-micro"
}
</code></pre>
<h3 id="heading-4-state-the-site-logbook">4. State (The Site Logbook)</h3>
<p>This is the most critical concept. When a construction crew builds a wall, they tick it off in the logbook. <em>"Wall A is done."</em></p>
<p>Terraform keeps a file called terraform.tfstate. This is the brain. It remembers what has already been built in the cloud.</p>
<ul>
<li><p>If you run the code today, it builds a database.</p>
</li>
<li><p>If you run the <em>exact same code</em> tomorrow, Terraform looks at the State File, sees the database exists, and <strong>does nothing</strong>.</p>
</li>
</ul>
<p>It is smart enough to know the difference between "Build a new house" and "The house is already there." This prevents you from accidentally billing yourself for 50 duplicate servers.</p>
<h2 id="heading-the-workflow-plan-vs-apply">The Workflow: Plan vs. Apply</h2>
<p>So, how do you actually run this thing? It’s a two-step dance that saves you from disaster.</p>
<h3 id="heading-step-1-terraform-plan-the-site-visit">Step 1: terraform plan (The Site Visit)</h3>
<p>You run this command in your terminal. Terraform reads your code, looks at your Google Cloud account, and compares them.</p>
<p>It then prints out a report: <em>"Okay boss. Based on this blueprint, I need to add 2 servers, change 1 firewall rule, and destroy 1 old bucket."</em></p>
<p>It hasn't touched anything yet. It is asking for permission. This is your chance to catch mistakes before they happen.</p>
<h3 id="heading-step-2-terraform-apply-groundbreaking">Step 2: terraform apply (Groundbreaking)</h3>
<p>If the plan looks good, you run terraform apply. The Google Cloud "crew" gets to work. APIs are called, resources are spun up, and within minutes, your infrastructure is live.</p>
<h2 id="heading-why-this-actually-matters">Why this actually matters</h2>
<p>I'm currently rebuilding the infrastructure for my e-commerce platform, <strong>Merch-KE</strong>, entirely in Terraform. Why go through the effort?</p>
<ol>
<li><p><strong>Sleep:</strong> I sleep better knowing that if I accidentally deleted my entire project today, I could run terraform apply and have the entire platform back online in 15 minutes.</p>
</li>
<li><p><strong>Audit Trails:</strong> Every change is a commit in GitHub. I can see exactly <em>who</em> changed the firewall rule and <em>when</em>.</p>
</li>
<li><p><strong>Speed:</strong> I don't waste time clicking menus. I write code, I push, it deploys.</p>
</li>
</ol>
<p>Stop treating your cloud infrastructure like a Jua Kali side hustle. Get a blueprint. Learn Terraform.</p>
<p><em>(If you want to see what a production-ready Terraform structure looks like, check out the</em> <a target="_blank" href="https://github.com/lxmwaniky/merch-ke-infra"><em>merch-ke-infra</em></a> <em>repo on my GitHub. It’s a work in progress, but the architecture is there.)</em></p>
<p><img src="https://github.com/lxmwaniky/merch-ke-infra/blob/main/merch-ke.png?raw=true" alt="merch-ke.png" /></p>
]]></content:encoded></item><item><title><![CDATA[How I Bagged the Google Cloud Professional Machine Learning Engineer Cert]]></title><description><![CDATA[NGL, this whole Machine Learning thing? It really ain’t my shit.

If you’ve followed my journey or read my previous posts, you know I’m a Software and Cloud Engineer through and through. I like building systems that have clear logic, predictable inte...]]></description><link>https://blog.lxmwaniky.me/pmle</link><guid isPermaLink="true">https://blog.lxmwaniky.me/pmle</guid><category><![CDATA[SkillCertExams PMLE]]></category><category><![CDATA[google cloud]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Tue, 06 Jan 2026 12:34:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767701153580/5b7f5402-93d6-46c8-b8b0-7bdc9bcbe489.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>NGL, this whole Machine Learning thing? It really ain’t my shit.</p>
<p><img src="https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExYm5wbTBtN3Q2NWsxemRoOG00MjhsdTJtMjUyeTJ1eXFzeXNkaHEwZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/ZfItzeyytsuRswub0v/giphy.gif" alt class="image--center mx-auto" /></p>
<p>If you’ve followed my journey or read my previous posts, you know I’m a Software and Cloud Engineer through and through. I like building systems that have clear logic, predictable integrations, and infrastructure that doesn’t require me to stare at loss curves until my eyes bleed. But here I am, a newly certified <a target="_blank" href="https://www.credly.com/earner/earned/badge/da7c30b8-e11e-4e59-9a75-4f6ad4e6829f"><strong>Google Cloud Professional Machine Learning Engineer</strong></a>. Honestly, it feels like a bit of a mistake, but it was a journey I needed to take.</p>
<p>It all started when I was watching <em>"The Thinking Game," a documentary by Google DeepMind (an absolute banger</em>). I’ve always done AI integrations—the "plug and play" stuff where you hit an API and magic happens—but that doc made me want to go deep into the rabbit hole. I wanted to understand the plumbing behind the curtain. I wanted to know how raw data actually transforms into "intelligence."</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=d95J8yzvjbQ">https://www.youtube.com/watch?v=d95J8yzvjbQ</a></div>
<p> </p>
<h3 id="heading-the-goal-and-the-hard-deadline">The Goal and the Hard Deadline</h3>
<p>I have a <a target="_blank" href="https://developers.google.com/program/plans-and-pricing">Google Developer Premium subscription</a>, which comes with a certification voucher. I’d set a personal goal to build a solid GCP foundation: I got the <a target="_blank" href="https://www.credly.com/earner/earned/badge/c90e0464-8a24-4dcb-822b-eda67c6c9144">Gen AI Leader cert</a> (Foundation), I’m currently prepping for the <a target="_blank" href="https://cloud.google.com/learn/certification/cloud-engineer">Associate Cloud Engineer</a> (Associate), and I wanted a Professional cert to round it out.</p>
<p>Since I had zero Christmas or New Year plans—no parties, no travel, just me and my laptop—I decided to schedule the PMLE. The catch? My voucher was expiring on the 30th of December. I had exactly one month to prepare for one of the most notoriously difficult exams in the Google ecosystem. It was a one-month sprint or bust.</p>
<h3 id="heading-the-burnout-and-the-upside-down">The Burnout and the "Upside Down"</h3>
<p>I started off strong. I dove into the <a target="_blank" href="https://www.skills.google/paths/17">Google Skills course</a>, learning about <strong>BigQueryML</strong>, <strong>Vertex AI Notebooks</strong>, and the basics of model training. But about three weeks out, I hit a wall. The content is massive. It’s not just about tools; it’s about a completely different dialect of engineering. My brain just checked out.</p>
<p>I literally gave up. I spent that entire week rewatching <em>Stranger Things</em> to prepare for the final season. (Side note: I’m still processing that Season 5 finale...). I was so far removed from ML that I didn't think I'd actually go back to it. I felt the burnout in my bones and was ready to let the voucher expire.</p>
<h3 id="heading-the-pivot-just-do-it">The Pivot: "Just Do It"</h3>
<p>One week before the exam, I snapped back. I hate leaving things unfinished. I reached out to my accountability partner, <a target="_blank" href="https://www.linkedin.com/in/beth-kimani-23605028b/">Beth</a>, to share my daily learnings. I told her basically every day, "Beth, I’m quitting. This isn't for me."</p>
<p>She was the realest. She didn't give me a motivational speech; she just said: "Just do it. The worst that can happen is a fail."</p>
<p>That’s when I locked in. I realised I didn't need to be a Keras or TensorFlow wizard to be a great Cloud Engineer. I decided to skip the deep coding and focus on <strong>AutoML</strong> and the <strong>Vertex AI</strong> ecosystem. On the 26th of December, <em>Stranger Things 5</em> dropped. I binged the whole thing, felt that "main character energy," and decided to read some blogs from people who’d actually survived the PMLE.</p>
<p>I found <a target="_blank" href="https://paulkamau.medium.com/i-passed-my-machine-learning-certification-exam-whats-next-6-things-you-should-do-next-317354b8379e">Paul Kamau’s blog</a> — it wasn't even a study guide, just a "what to do after" post, but it gave me the mental closure I needed to just finish the task before my birthday on the 31st. I even read his <a target="_blank" href="https://paulkamau.medium.com/i-failed-my-machine-learning-certification-test-now-what-df412b95fbb3">post on failing</a> to make peace with the possibility of a "No Pass" result.</p>
<h3 id="heading-the-48-hour-vertex-ai-deep-dive">The 48-Hour Vertex AI Deep Dive</h3>
<p>Two days before the exam, I found a post on the <a target="_blank" href="https://discuss.google.dev/t/google-clouds-professional-ml-engineer-pmle-exam-how-i-passed-in-30-days-and-you-can-too/179510">Google Developer Forums</a> that was a total game-changer. It emphasized that <strong>60-70% of the exam is Vertex AI.</strong> I stopped guessing and spent 48 hours straight reading the documentation for these specific pillars:</p>
<ul>
<li><p><a target="_blank" href="https://cloud.google.com/vertex-ai/docs/beginner/beginners-guide"><strong>Vertex AI AutoML</strong></a><strong>:</strong> The "no-code" hero. It handles feature engineering and model selection for you, making it perfect for rapid prototyping.</p>
</li>
<li><p><a target="_blank" href="https://cloud.google.com/vertex-ai/docs/pipelines/introduction"><strong>Vertex AI Pipelines &amp; Orchestration</strong></a><strong>:</strong> The factory line. This is how you use Kubeflow to automate the entire "Data -&gt; Train -&gt; Deploy" workflow.</p>
</li>
<li><p><a target="_blank" href="https://cloud.google.com/vertex-ai/docs/experiments/intro-vertex-ai-experiments"><strong>Vertex AI Experiments</strong></a> <strong>&amp;</strong> <a target="_blank" href="https://cloud.google.com/vertex-ai/docs/ml-metadata/introduction"><strong>Metadata</strong></a><strong>:</strong> The forensics lab. Experiments track your scores (accuracy/loss), while Metadata tracks the lineage—proving exactly which dataset created which model version.</p>
</li>
<li><p><a target="_blank" href="https://cloud.google.com/vertex-ai/docs/model-registry/introduction"><strong>Vertex AI Model Registry</strong></a> <strong>&amp;</strong> <a target="_blank" href="https://cloud.google.com/vertex-ai/docs/general/deployment"><strong>Endpoints</strong></a><strong>:</strong> The library and the URL. This is where you version your models and deploy them to an IP address so they can serve live traffic.</p>
</li>
<li><p><a target="_blank" href="https://cloud.google.com/vertex-ai/docs/featurestore"><strong>Vertex AI Feature Store</strong></a><strong>:</strong> The central hub. It allows different teams to share and reuse data features without recalculating them every time.</p>
</li>
<li><p><a target="_blank" href="https://cloud.google.com/vertex-ai/docs/explainable-ai/overview"><strong>Vertex AI Explainable AI (XAI)</strong></a><strong>:</strong> The "Why." Using Feature Attributions to explain exactly why a model made a certain decision.</p>
</li>
<li><p><a target="_blank" href="https://cloud.google.com/vertex-ai/docs/model-monitoring/overview"><strong>Vertex AI Model Monitoring</strong></a><strong>:</strong> The watchdog. It checks for <strong>Drift</strong> (when the world changes) or <strong>Skew</strong> (when your training data doesn't match real-life data).</p>
</li>
</ul>
<p>I used <a target="_blank" href="https://gemini.google.com/">Gemini</a> to generate scenario questions. The trick to Google exams is looking for keywords: if the prompt says "minimise cost for non-urgent tasks," the answer is Batch Prediction. If it says "fastest time to market," it’s AutoML.</p>
<h3 id="heading-exam-day-20-minutes-of-pure-chaos">Exam Day: 20 Minutes of Pure Chaos</h3>
<p>I scheduled the exam for 10:00 AM. I woke up at 9:40 AM.</p>
<p><img src="https://media1.giphy.com/media/v1.Y2lkPTc5MGI3NjExbWlic3MxbGZvbmJrYm53a3hjbjg1ZGx1am04dnRucWRnZG9mcDlnZCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/9bXjoBk28pwnKLKoKb/giphy.gif" alt class="image--center mx-auto" /></p>
<p>I was confused, panicked, and lowkey terrified. I took a fast shower and jumped into the portal with minutes to spare. For the first 30 minutes, I only managed to finish 10 questions. My brain hadn't fully woken up, and I was marking <em>everything</em> for review because I was so unsure of myself.</p>
<p>But then, the pattern clicked. I started seeing the "Google Way"—prioritising efficiency, cost-saving, and managed services. I finished the 50 questions with 30 minutes to spare and went back to review every single answer. I hit submit, expecting to see a failure screen.</p>
<p><strong>"PASS."</strong></p>
<p>I smiled, thanked God, and went straight back to sleep.</p>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>At times, I feel like a bit of an imposter because ML really isn't my primary focus and I don't plan on chasing ML-heavy roles. I’m a Software and Cloud Engineer, and that’s where I’m staying. However, I satisfied my curiosity. I went down the rabbit hole and came back with a professional certification that proves I understand the architecture of modern AI.</p>
<p>If you’re prepping for this and feel like it’s "not your thing," don't trip. Lock in on Vertex AI, find an accountability partner to keep you from quitting, and remember: the worst that can happen is a fail. And even then, you’ve still learned how the world works behind the scenes.</p>
<p>Now, back to the ACE prep. See ya in the cloud. ✌️</p>
]]></content:encoded></item><item><title><![CDATA[My First Steps into BigQuery ML]]></title><description><![CDATA[I spent yesterday learning BigQuery ML, and honestly? I'm still buzzing from it. Not because it was easy (it wasn't), but because for the first time, machine learning felt... achievable. Let me walk you through what I learnt, where I stumbled, and wh...]]></description><link>https://blog.lxmwaniky.me/my-first-steps-into-bigquery-ml</link><guid isPermaLink="true">https://blog.lxmwaniky.me/my-first-steps-into-bigquery-ml</guid><category><![CDATA[Machine Learning]]></category><category><![CDATA[BigQuery ML]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Tue, 02 Dec 2025 21:30:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764710833846/7262ca3c-5374-4744-9789-11ffa44dbff0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I spent yesterday learning BigQuery ML, and honestly? I'm still buzzing from it. Not because it was easy (it wasn't), but because for the first time, machine learning felt... achievable. Let me walk you through what I learnt, where I stumbled, and what finally clicked.</p>
<h2 id="heading-the-starting-point-what-even-is-this">The Starting Point: "What Even Is This?"</h2>
<p>I opened BigQuery with one goal: learn ML. I'd heard people throw around terms like "machine learning models" and "predictive analytics" for ages, and I always nodded along pretending I got it. Today, I decided to actually understand it.</p>
<p>The first thing I learnt? Machine learning isn't magic. It's pattern recognition. That's it. You show a computer loads of examples, and it figures out the patterns. Then when you give it new data, it uses those patterns to make predictions.</p>
<p>Think of it like this: If I showed you 1,000 photos of my campus and told you which ones were taken during exam season (empty library, stressed faces, Red Bull cans everywhere) versus holiday season (packed social spaces, smiling faces, general chaos), you'd eventually spot the patterns. You could then look at a new photo and guess when it was taken. That's basically what ML does, just with numbers instead of photos.</p>
<h2 id="heading-the-three-types-of-ml-i-covered">The Three Types of ML I Covered</h2>
<h3 id="heading-1-regression-predicting-numbers">1. Regression: Predicting Numbers</h3>
<p>This is when you want to predict an actual number. How much will this house cost? How long will this taxi ride take? What will the temperature be tomorrow?</p>
<p>I practised with NYC taxi data, trying to predict trip duration. The model looked at things like:</p>
<ul>
<li><p>Distance of the trip</p>
</li>
<li><p>Time of day</p>
</li>
<li><p>Day of the week</p>
</li>
<li><p>Pickup and dropoff locations</p>
</li>
</ul>
<p>And from all that, it predicted: "This trip will probably take 847 seconds."</p>
<p><strong>Where I messed up:</strong> At first, I didn't clean the data properly. I had trips that supposedly took 4 minutes but covered 0 miles. Rubbish data = rubbish predictions. Lesson learnt: Always check your data makes sense before training anything.</p>
<h3 id="heading-2-classification-putting-things-in-boxes">2. Classification: Putting Things in Boxes</h3>
<p>This is when you want to categorise something. Will this customer buy again? (Yes/No) Is this email spam? (Yes/No) What type of product is this? (Electronics/Clothing/Food)</p>
<p>I worked with e-commerce data, trying to predict: "Will this visitor return and make a purchase?" The model looked at their first visit:</p>
<ul>
<li><p>Did they bounce immediately?</p>
</li>
<li><p>How long did they stay?</p>
</li>
<li><p>How many pages did they view?</p>
</li>
<li><p>Where did they come from? (Google, Facebook, direct link)</p>
</li>
<li><p>What device were they on?</p>
</li>
</ul>
<p>Then it predicted: "This person has an 85% chance of buying when they come back."</p>
<p><strong>Where I messed up:</strong> I initially trained and tested on the same data. It's like studying for an exam using the exact questions that'll be on the paper—of course you'll do well, but you haven't actually learnt anything. You need separate data for training and testing.</p>
<h3 id="heading-3-clustering-finding-hidden-groups">3. Clustering: Finding Hidden Groups</h3>
<p>This is different. You're not predicting anything. You're asking: "What natural groups exist in my data?"</p>
<p>Imagine you have data on 10,000 customers but no labels. Clustering looks at their behaviour and says: "I've found 5 distinct groups":</p>
<ul>
<li><p>Budget shoppers who only buy during sales</p>
</li>
<li><p>Premium customers who buy expensive items regularly</p>
</li>
<li><p>Window shoppers who browse but rarely purchase</p>
</li>
<li><p>Loyal mid-tier customers</p>
</li>
<li><p>Brand new explorers still figuring things out</p>
</li>
</ul>
<p>Nobody told the model these groups existed. It discovered them by finding similar patterns.</p>
<h2 id="heading-the-sql-parts-that-confused-me">The SQL Parts That Confused Me</h2>
<p>I thought I knew SQL. Turns out, I knew basic SQL. BigQuery ML threw some curveballs.</p>
<h3 id="heading-unnest-the-array-flattener">UNNEST: The Array Flattener</h3>
<p>This one broke my brain initially. The Google Analytics data was nested—like a box inside a box inside another box. Each website session contained multiple "hits" (page views), and each hit contained multiple products.</p>
<pre><code class="lang-sql">FROM table,
UNNEST(hits) AS h,
UNNEST(h.product) AS p
</code></pre>
<p>What this does: It takes all those nested boxes and lays everything out flat so you can actually work with it. Session → hits → products becomes individual rows you can query.</p>
<p><strong>Analogy:</strong> Imagine you have a filing cabinet (session) with folders (hits) that contain documents (products). UNNEST takes all the documents out and spreads them on your desk so you can see everything at once.</p>
<h3 id="heading-group-by-the-you-must-include-everything-rule">GROUP BY: The "You Must Include Everything" Rule</h3>
<p>This rule annoyed me until I understood it. Here's the thing:</p>
<p>If you're using aggregate functions like SUM() or COUNT(), SQL needs to know: "Sum WHAT together?"</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> 
  product_name,           <span class="hljs-comment">-- Not aggregated</span>
  <span class="hljs-keyword">SUM</span>(quantity)           <span class="hljs-comment">-- Aggregated</span>
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">table</span>
</code></pre>
<p>SQL says: "You're summing quantities into ONE number, but showing multiple product names? Which name goes with the total? I'm confused!"</p>
<p>You need GROUP BY:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> 
  product_name,
  <span class="hljs-keyword">SUM</span>(quantity)
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">table</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> product_name    <span class="hljs-comment">-- Now I know: sum quantities for EACH product</span>
</code></pre>
<p><strong>My mistake:</strong> I kept forgetting to include columns in GROUP BY and got errors like "expression references column which is neither grouped nor aggregated." Now I get it—every column that's not in a SUM/COUNT/AVG/etc. must be in GROUP BY.</p>
<h3 id="heading-if-and-case-making-decisions-in-sql">IF and CASE: Making Decisions in SQL</h3>
<p>These let you add logic to your queries.</p>
<p><strong>IF</strong> is simple: "If this is true, do this, otherwise do that"</p>
<pre><code class="lang-sql">IF(count &gt; 0, 1, 0)
<span class="hljs-comment">-- If count is greater than 0, return 1, otherwise return 0</span>
</code></pre>
<p><strong>CASE</strong> is like multiple IF statements:</p>
<pre><code class="lang-sql">CASE
  WHEN score &gt; 90 THEN 'Excellent'
  WHEN score &gt; 70 THEN 'Good'
  WHEN score &gt; 50 THEN 'Average'
  ELSE 'Needs work'
<span class="hljs-keyword">END</span>
</code></pre>
<p><strong>Where I got confused:</strong> In one query, I saw <code>&gt; 0, 1, 0</code> and thought there were three zeros doing the same thing. Took me ages to realise:</p>
<ul>
<li><p>First <code>0</code> is checking if something is greater than zero (part of the question)</p>
</li>
<li><p>Second number <code>1</code> is what to return if true</p>
</li>
<li><p>Third number <code>0</code> is what to return if false</p>
</li>
</ul>
<p>Three different jobs, just unlucky they were all zeros!</p>
<h2 id="heading-the-complete-workflow-what-actually-happens">The Complete Workflow: What Actually Happens</h2>
<p>Here's what I learnt the ML process actually looks like:</p>
<h3 id="heading-step-1-explore-your-data">Step 1: Explore Your Data</h3>
<p>Look at what you've got. Check for weird values. Understand what each column means.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">table</span> <span class="hljs-keyword">LIMIT</span> <span class="hljs-number">10</span>;
</code></pre>
<p>Simple, but essential.</p>
<h3 id="heading-step-2-clean-your-data">Step 2: Clean Your Data</h3>
<p>Remove the rubbish. Filter out impossible values.</p>
<p><strong>My data had:</strong></p>
<ul>
<li><p>Taxi trips with zero distance (how?)</p>
</li>
<li><p>Negative durations (time travel?)</p>
</li>
<li><p>Trips over 100 miles (from NYC to where??)</p>
</li>
</ul>
<p>All had to go.</p>
<h3 id="heading-step-3-create-features">Step 3: Create Features</h3>
<p>This is where you get creative. Take raw data and turn it into useful information.</p>
<p>Instead of just storing <code>pickup_datetime</code>, I extracted:</p>
<ul>
<li><p>Hour of day (rush hour vs quiet time)</p>
</li>
<li><p>Day of week (weekday vs weekend)</p>
</li>
</ul>
<p>These features help the model spot patterns.</p>
<h3 id="heading-step-4-split-your-data">Step 4: Split Your Data</h3>
<p><strong>This is crucial.</strong> Split into:</p>
<ul>
<li><p>Training data (60-80%): Model learns from this</p>
</li>
<li><p>Test data (20-40%): Model has never seen this—used to check if it actually works</p>
</li>
</ul>
<p><strong>My dates:</strong></p>
<ul>
<li><p>Training: August 2016 - April 2017</p>
</li>
<li><p>Testing: May - June 2017</p>
</li>
<li><p>Predictions: July 2017 onwards</p>
</li>
</ul>
<p>No overlap! That's the key.</p>
<h3 id="heading-step-5-train-the-model">Step 5: Train the Model</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">OR</span> <span class="hljs-keyword">REPLACE</span> <span class="hljs-keyword">MODEL</span> <span class="hljs-string">`project.dataset.model_name`</span>
OPTIONS(
  model_type=<span class="hljs-string">'logistic_reg'</span>,
  input_label_cols=[<span class="hljs-string">'what_youre_predicting'</span>]
)
<span class="hljs-keyword">AS</span>
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> training_data;
</code></pre>
<p>This is where the magic happens. BigQuery analyses all your training data and figures out the patterns.</p>
<h3 id="heading-step-6-evaluate-the-model">Step 6: Evaluate the Model</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> ML.EVALUATE(<span class="hljs-keyword">MODEL</span> model_name, (test_data));
</code></pre>
<p>This tells you: "How good is your model?"</p>
<p>For classification, you get a score called <code>roc_auc</code>:</p>
<ul>
<li><p>0.9+ = Excellent</p>
</li>
<li><p>0.8-0.9 = Good</p>
</li>
<li><p>0.7-0.8 = Decent</p>
</li>
<li><p>0.6-0.7 = Poor</p>
</li>
<li><p>0.5 = Literally just guessing</p>
</li>
</ul>
<p>My first model got 0.72 (decent). My improved model with more features got 0.91 (excellent!).</p>
<h3 id="heading-step-7-make-predictions">Step 7: Make Predictions</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> ML.PREDICT(<span class="hljs-keyword">MODEL</span> model_name, (new_data));
</code></pre>
<p>Now you can predict on completely new data. This is where it becomes useful in the real world.</p>
<h2 id="heading-what-improved-my-model">What Improved My Model</h2>
<p>My first classification model only looked at two things:</p>
<ul>
<li><p>Did they bounce?</p>
</li>
<li><p>How long did they stay?</p>
</li>
</ul>
<p>Score: 0.72 (decent)</p>
<p>My second model added:</p>
<ul>
<li><p>Number of pages viewed</p>
</li>
<li><p>How far they got in the shopping process (viewed products? Added to cart? Checked out?)</p>
</li>
<li><p>Where they came from (Google? Facebook? Email?)</p>
</li>
<li><p>What device they used (mobile? desktop?)</p>
</li>
<li><p>What country they were from</p>
</li>
</ul>
<p>Score: 0.91 (excellent!)</p>
<p><strong>The lesson:</strong> More relevant features = better predictions. But only relevant ones—adding their visitor ID wouldn't help because it's just a random identifier that doesn't tell you anything about behaviour.</p>
<h2 id="heading-the-metrics-that-matter">The Metrics That Matter</h2>
<h3 id="heading-for-regression-predicting-numbers">For Regression (predicting numbers):</h3>
<p><strong>Mean Absolute Error (MAE):</strong> On average, how far off are your predictions?</p>
<ul>
<li><p>My taxi model: 239 seconds (about 4 minutes)</p>
</li>
<li><p>For a taxi ride, that's pretty good</p>
</li>
</ul>
<p><strong>R² Score:</strong> How much of the variation does your model explain? (0 to 1)</p>
<ul>
<li><p>My model: 0.72 (explains 72% of the variation)</p>
</li>
<li><p>Not perfect, but solid</p>
</li>
</ul>
<h3 id="heading-for-classification-predicting-categories">For Classification (predicting categories):</h3>
<p><strong>ROC AUC:</strong> Overall accuracy measure (0 to 1)</p>
<ul>
<li><p>Higher is better</p>
</li>
<li><p>0.91 = excellent</p>
</li>
</ul>
<p><strong>Precision vs Recall:</strong> Trade-offs in classification</p>
<ul>
<li><p>Precision: Of the ones you predicted "yes", how many were actually "yes"?</p>
</li>
<li><p>Recall: Of all the actual "yes" cases, how many did you catch?</p>
</li>
</ul>
<h2 id="heading-where-i-failed-and-what-i-learnt">Where I Failed (And What I Learnt)</h2>
<h3 id="heading-failure-1-training-and-testing-on-the-same-data">Failure 1: Training and Testing on the Same Data</h3>
<p>I got amazing results! Model was perfect! Then I realised I'd basically given it the answers during the test. Rookie mistake. Always use separate time periods or random splits.</p>
<h3 id="heading-failure-2-not-checking-data-quality-first">Failure 2: Not Checking Data Quality First</h3>
<p>Garbage in, garbage out. I trained a model on data with impossible values and wondered why predictions were weird. Now I always clean first.</p>
<h3 id="heading-failure-3-forgetting-to-handle-missing-values">Failure 3: Forgetting to Handle Missing Values</h3>
<p>SQL doesn't like NULLs. I kept getting errors until I learnt about <code>IFNULL()</code>:</p>
<pre><code class="lang-sql">IFNULL(column_name, 0)  <span class="hljs-comment">-- If it's NULL, use 0 instead</span>
</code></pre>
<h3 id="heading-failure-4-not-understanding-my-own-sql">Failure 4: Not Understanding My Own SQL</h3>
<p>I'd copy-paste queries without understanding them. Then when something broke, I had no idea why. Typing queries out line by line (even if slower) made me actually understand what was happening.</p>
<h2 id="heading-what-finally-clicked">What Finally Clicked</h2>
<p>The moment it all made sense was when I realised: <strong>ML is just finding patterns in examples.</strong></p>
<p>That's it. You're not teaching it rules. You're showing it loads of examples and letting it figure out what matters.</p>
<ul>
<li><p>Lots of people who bounce don't come back? Pattern spotted.</p>
</li>
<li><p>People who view 5+ pages tend to buy? Pattern spotted.</p>
</li>
<li><p>Mobile users from social media behave differently to desktop users from Google? Pattern spotted.</p>
</li>
</ul>
<p>The model just finds and remembers these patterns, then applies them to new data.</p>
<h2 id="heading-whats-next">What's Next?</h2>
<p>I want to try this with Kenyan data. Something relevant to home:</p>
<ul>
<li><p>Predicting crop yields based on rainfall and temperature?</p>
</li>
<li><p>Classifying M-Pesa transactions as genuine or fraudulent?</p>
</li>
<li><p>Clustering Nairobi neighbourhoods by economic activity?</p>
</li>
</ul>
<p>The possibilities are endless once you understand the basics.</p>
<h2 id="heading-key-takeaways-for-future-me">Key Takeaways (For Future Me)</h2>
<ol>
<li><p><strong>Clean your data first.</strong> Always. No exceptions.</p>
</li>
<li><p><strong>Split your data properly.</strong> Train on old data, test on new data, predict on future data. Never mix them up.</p>
</li>
<li><p><strong>Start simple, then improve.</strong> My first model had 2 features and was decent. My second had 11 features and was excellent. Build iteratively.</p>
</li>
<li><p><strong>Understand your SQL.</strong> Type it out, don't just copy-paste. The errors you make will teach you more than getting it right the first time.</p>
</li>
<li><p><strong>More features ≠ are always better.</strong> They need to be relevant. Adding random IDs or useless columns just confuses the model.</p>
</li>
<li><p><strong>Model scores don't mean much without context.</strong> 0.72 might be excellent for some problems and terrible for others. Understand what you're trying to achieve.</p>
</li>
<li><p><strong>The workflow matters more than memorising syntax.</strong> Explore → Clean → Feature Engineering → Split → Train → Evaluate → Predict → Deploy. Master this process, and you can build anything.</p>
</li>
</ol>
<h2 id="heading-resources-that-helped">Resources That Helped</h2>
<ul>
<li><p>Google's public BigQuery datasets (perfect for practising)</p>
</li>
<li><p>The NYC taxi data (messy real-world data)</p>
</li>
<li><p>Google Analytics e-commerce data (nested data practice)</p>
</li>
<li><p>Patience (lots of it)</p>
</li>
<li><p>Trial and error (even more of it)</p>
</li>
</ul>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Machine learning felt like this huge, intimidating thing I'd never understand. Turns out, it's just SQL with some extra steps. If you can write queries, clean data, and think logically about patterns, you can do this.</p>
<p>Future me: When you're revising for exams and come back to this post, remember—you learnt all of this in one day. You got confused, you made mistakes, you fixed them. That's how learning works. Don't stress about not remembering everything. Just start building something, and it'll come back to you.</p>
]]></content:encoded></item><item><title><![CDATA[Got Certified as a Google Cloud Generative AI Leader]]></title><description><![CDATA[I'm thrilled to share that I recently earned my Google Cloud Generative AI Leader certification! It's been an incredible journey packed into seven days of focused preparation, and I want to walk you through exactly how I did it.

But first, let me ex...]]></description><link>https://blog.lxmwaniky.me/gen-ai-leader</link><guid isPermaLink="true">https://blog.lxmwaniky.me/gen-ai-leader</guid><category><![CDATA[generative ai]]></category><category><![CDATA[generative AI certification]]></category><category><![CDATA[google cloud]]></category><category><![CDATA[GenAI Cohort]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Thu, 27 Nov 2025 18:14:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764265188328/3552be59-5a8f-4a6f-82f5-04da48173d30.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I'm thrilled to share that I recently earned my Google Cloud <a target="_blank" href="https://cloud.google.com/learn/certification/generative-ai-leader">Generative AI Leader</a> certification! It's been an incredible journey packed into seven days of focused preparation, and I want to walk you through exactly how I did it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764265146457/5e0da67a-6461-430b-89ea-1898874570d6.png" alt class="image--center mx-auto" /></p>
<p>But first, let me explain what this certification actually means. A Generative AI Leader is someone who understands how generative AI can genuinely transform businesses. It's not about being a technical wizard who can code AI models from scratch. Instead, it's about having that business-level knowledge of Google Cloud's gen AI offerings and understanding Google's AI-first approach well enough to guide organisations towards innovative and responsible AI adoption. You're essentially someone who can spot opportunities across different business functions and industries, then influence gen AI-powered initiatives using Google Cloud's enterprise-ready offerings.</p>
<p>What makes this certification particularly valuable is that it's designed for anyone, regardless of their job role or technical background. Whether you're in marketing, operations, finance, or even a non-technical leadership position, this certification proves you can bridge the gap between AI technology and business strategy. In a world where every company is scrambling to figure out their AI strategy, being able to speak both languages is incredibly powerful.</p>
<h2 id="heading-why-i-decided-to-take-this-exam">Why I Decided to Take This Exam</h2>
<p>Honestly, the decision came from watching how rapidly generative AI is reshaping entire industries. Every conversation I had, every article I read, and every business decision I observed seemed to circle back to gen AI. I realised that understanding this technology at a strategic level wasn't optional anymore. It was essential. I wanted to position myself as someone who could not only understand the hype but also cut through it and identify real, practical applications of generative AI in business contexts.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764265882480/04661994-d489-4465-b593-14e8fa5eddf0.jpeg" alt class="image--center mx-auto" /></p>
<p>Plus, Google Cloud has been making significant moves in the AI space, and I was curious to understand their approach more deeply. The certification seemed like the perfect way to gain that structured knowledge whilst also having something tangible to show for it.</p>
<h2 id="heading-my-one-week-preparation-journey">My One-Week Preparation Journey</h2>
<p>Let me be upfront about this. One week is tight. Really tight. But it's doable if you're strategic about your time and resources. I treated it like a sprint, dedicating several hours each day to studying. Here's how I broke it down.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764266000478/0094137f-4749-4d6d-95dc-f9121aeda991.jpeg" alt class="image--center mx-auto" /></p>
<p>The first couple of days, I immersed myself in the fundamentals. I needed to ensure I had a solid grasp of what generative AI actually is, how it differs from traditional machine learning, and what makes it so transformative. I spent time understanding concepts like large language models, prompt engineering, and the various techniques used to improve model outputs.</p>
<p>By midweek, I shifted my focus to Google Cloud's specific offerings. This meant diving into Vertex AI, understanding the different AI tools and services Google provides, and learning how they fit together in real-world scenarios. I made sure I understood not just what each service does, but when and why you'd use one over another.</p>
<p>The final stretch was all about application and business strategy. I worked through scenario-based practice questions because I'd heard the exam was heavy on practical application. This turned out to be absolutely crucial preparation, as the actual exam was indeed focused on real-world business scenarios rather than abstract technical knowledge.</p>
<h2 id="heading-the-resources-that-made-the-difference">The Resources That Made the Difference</h2>
<p>I relied heavily on Google's own materials, and I'm genuinely impressed by how comprehensive and well-structured they are. Here's what I used and how each resource helped me.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1764266101688/abbe2c62-8d00-4e71-8930-aca341cd1895.jpeg" alt class="image--center mx-auto" /></p>
<p><strong>The</strong> <a target="_blank" href="https://services.google.com/fh/files/misc/generative_ai_leader_study_guide_english.pdf"><strong>Study Guide</strong></a> was my roadmap. Google provides a detailed study guide that outlines everything you need to know for the exam. I started here because it gave me a clear picture of the scope. I'd recommend printing it out or having it open on a second screen whilst you study, so you can tick off topics as you cover them. It helped me ensure I wasn't missing any critical areas.</p>
<p><strong>The</strong> <a target="_blank" href="https://www.skills.google/paths/1951"><strong>Generative AI Leader Learning Path</strong></a> on the Google Skills Platform(formerly Cloud Skills Boost) was essential. This is a free course that walks you through all the key concepts you need. I went through every module carefully, taking notes as I went. The course is structured brilliantly, starting with fundamentals and gradually building up to more complex business applications. What I particularly appreciated was how it connected technical concepts to real business outcomes. You're not just learning what a language model is; you're learning how it can solve actual business problems.</p>
<p><strong>The</strong> <a target="_blank" href="https://services.google.com/fh/files/misc/generative_ai_leader_exam_guide_english.pdf"><strong>Exam Guide</strong></a> complemented the study guide by showing me exactly what the exam would assess. It breaks down the knowledge areas into four main sections: fundamentals of gen AI, Google Cloud's gen AI offerings, techniques to improve gen AI model output, and business strategies for successful gen AI solutions. I used this to prioritise my study time, spending more hours on areas where I felt less confident.</p>
<p><strong>The</strong> <a target="_blank" href="https://forms.gle/soztS7Q74AXBncATA"><strong>Sample Questions</strong></a> were a game changer. They helped me understand not just what topics would be covered, but how questions would be framed. The exam is all about scenarios and application, so practising with these sample questions helped me get into the right mindset. I didn't just answer them; I made sure I understood why each correct answer was right and why the wrong answers were wrong.</p>
<p>One thing I'll mention is that I also explored the Google Cloud community on <a target="_blank" href="https://www.reddit.com/r/googlecloud/">Reddit</a>. Whilst I didn't rely on it as a primary study resource, reading about other people's experiences gave me useful insights into what to expect and where to focus my attention.</p>
<h2 id="heading-what-the-exam-was-actually-like">What the Exam Was Actually Like</h2>
<p>The exam was 90 minutes long with 45 multiple-choice questions. That might sound like plenty of time, but some of those questions require careful thought. You're given scenarios and asked to identify the best approach, the most appropriate Google Cloud service, or the right strategy for a particular business challenge.</p>
<p>Here's what really stood out to me: nearly every question was scenario-based. You won't find many questions asking you to simply define a term or list features. Instead, you'll see questions like, "A retail company wants to personalise customer recommendations whilst ensuring data privacy. What's the best approach?" or "Your organisation is concerned about bias in AI-generated content. Which technique should you prioritise?"</p>
<p>This means understanding how gen AI applies to real-world problems is absolutely key. You need to think about business context, not just technology. Questions touched on various industries like healthcare, finance, retail, and manufacturing, so having a broad understanding of how gen AI can be applied across different sectors is important.</p>
<p>I also noticed a strong emphasis on responsible AI. Google clearly wants Generative AI Leaders to understand not just what's possible with the technology, but what's ethical and responsible. Questions about managing bias, ensuring transparency, protecting privacy, and maintaining security came up regularly.</p>
<h2 id="heading-key-topics-to-focus-on">Key Topics to Focus On</h2>
<p>The exam guide outlines four main knowledge areas, and I'd recommend giving each of them serious attention.</p>
<p><strong>Fundamentals of Gen AI</strong> covers the basics. You need to understand what generative AI is, how it differs from traditional machine learning, and what makes it so powerful. This includes concepts like large language models, transformers, tokens, embeddings, and the various types of generative models (text, image, code, etc.). Don't just memorise definitions; make sure you understand the underlying concepts and how they relate to each other.</p>
<p><strong>Google Cloud's Gen AI Offerings</strong> is where you learn about the specific tools and services. You'll need to know about Vertex AI and its various components, Model Garden, Generative AI Studio, and the different ways Google enables organisations to build with gen AI. Understand what each service does and, more importantly, when you'd use each one. Pay attention to the differences between pre-trained models, fine-tuned models, and building custom models.</p>
<p><strong>Techniques to Improve Gen AI Model Output</strong> dives into how you make these models work better for your specific needs. This includes prompt engineering (probably one of the most practically important skills), retrieval-augmented generation (RAG), fine-tuning, grounding, and various optimisation techniques. You'll want to understand not just what each technique does, but when and why you'd use one approach over another. For instance, when is fine-tuning worth the effort versus just improving your prompts? When should you use RAG to ground your model in specific data?</p>
<p><strong>Business Strategies for Successful Gen AI Solutions</strong> brings everything together from a strategic perspective. This covers how to identify opportunities for gen AI in your organisation, build business cases, manage change, address concerns about AI adoption, ensure responsible use, and measure success. This section is less about the technology itself and more about being an effective leader who can drive gen AI initiatives forward.</p>
<h2 id="heading-my-top-study-tips">My Top Study Tips</h2>
<p>Based on my experience, here are the things that made the biggest difference in my preparation.</p>
<p><strong>Focus on application over memorisation.</strong> You won't succeed by just memorising definitions and features. Instead, practice thinking through scenarios. When you learn about a new concept or tool, immediately ask yourself: "Where would this be useful? What problems does it solve? When would I choose this over alternatives?" This mindset shift made everything click for me.</p>
<p><strong>Pay attention to keywords in questions.</strong> The exam questions are carefully worded, and specific keywords often point you towards the right answer. Words like "immediately," "cost-effective," "scalable," "secure," or "responsible" aren't there by accident. They're guiding you towards understanding what the scenario is prioritising. I found it helpful to underline these keywords as I read through practice questions.</p>
<p><strong>Understand the full spectrum of gen AI approaches.</strong> There's often a temptation to think that more sophisticated techniques are always better. But sometimes a simple prompt is all you need, and fine-tuning a model would be overkill. Understanding when to use lighter-touch approaches versus heavier investments in customisation is crucial. The exam tests your ability to match the right tool to the right job.</p>
<p><strong>Don't skip the</strong> <a target="_blank" href="https://www.skills.google/paths/1951"><strong>Google Skills course</strong></a><strong>.</strong> It's free, it's comprehensive, and it's genuinely well done. I found that the course alone covered most of what I needed to know. The structure helps you build knowledge progressively, and the examples are practical and relevant.</p>
<p><strong>Think about responsible AI from the start.</strong> This isn't just an add-on topic; it's woven throughout the entire exam. Google clearly wants Generative AI Leaders to champion responsible AI practices. Whenever you're thinking through a scenario, consider the ethical implications, potential biases, privacy concerns, and transparency requirements.</p>
<p><strong>Practice explaining concepts in business terms.</strong> Since this is a leadership certification, you need to be comfortable translating technical concepts into business value. Practice explaining why something matters to a business leader who doesn't have a technical background. This skill will serve you well both in the exam and in actual leadership roles.</p>
<p><strong>Use the official materials first.</strong> There are lots of study resources out there, but I found Google's official materials to be the most reliable and comprehensive. Start with those, and only branch out if you need additional perspectives on specific topics.</p>
<h2 id="heading-final-thoughts-and-resources">Final Thoughts and Resources</h2>
<p>Looking back on this week-long journey, I'm genuinely grateful for how accessible Google has made this certification. The fact that the core learning path is completely free removes a major barrier, and the quality of the materials shows that Google is serious about building a community of AI-informed leaders.</p>
<p>The $99 exam fee is reasonable, and the certification is valid for three years, providing ample time to leverage it before renewal is required. For anyone considering this certification, I'd say go for it. Whether you're in a technical role looking to develop business acumen or a business professional wanting to understand AI strategy, this certification provides genuine value.</p>
<p>Here are all the official resources I used and would recommend:</p>
<p><strong>Study Guide:</strong> <a target="_blank" href="https://services.google.com/fh/files/misc/generative_ai_leader_study_guide_english.pdf">https://services.google.com/fh/files/misc/generative_ai_leader_study_guide_english.pdf</a></p>
<p><strong>Generative AI Leader Learning Path (Free):</strong> <a target="_blank" href="https://www.skills.google/paths/1951">https://www.skills.google/paths/1951</a></p>
<p><strong>Exam Guide:</strong> <a target="_blank" href="https://services.google.com/fh/files/misc/generative_ai_leader_exam_guide_english.pdf">https://services.google.com/fh/files/misc/generative_ai_leader_exam_guide_english.pdf</a></p>
<p><strong>Sample Exam Questions:</strong> <a target="_blank" href="https://forms.gle/soztS7Q74AXBncATA">https://forms.gle/soztS7Q74AXBncATA</a></p>
<p><strong>Schedule Your Exam:</strong> <a target="_blank" href="https://cp.certmetrics.com/google/en/login">https://cp.certmetrics.com/google/en/login</a></p>
<p><strong>Certification Details:</strong> <a target="_blank" href="https://cloud.google.com/learn/certification/generative-ai-leader">https://cloud.google.com/learn/certification/generative-ai-leader</a></p>
<p>The materials Google provides are genuinely excellent, and I found them more than sufficient for passing the exam. The learning path is structured thoughtfully, the study guide is comprehensive, and the sample questions give you a real feel for what to expect.</p>
<p>If you're on the fence about taking this certification, my advice is simple: if you work with technology, business strategy, or leadership in any capacity, this knowledge is becoming essential. The certification is achievable with focused preparation, and the knowledge you gain will serve you well beyond just passing an exam. Generative AI isn't going anywhere; it's only going to become more central to how businesses operate. Positioning yourself as someone who understands both the technology and its business applications is one of the smartest moves you can make right now.</p>
<p>Good luck with your preparation, and feel free to reach out if you have any questions about the exam or the journey</p>
]]></content:encoded></item><item><title><![CDATA[From Attendee to Organizer: How DevFest Mt. Kenya 2025 Reminded Me Why Communities Matter]]></title><description><![CDATA[Look, I'm not gonna lie. Standing backstage at DevFest Mt. Kenya 2025, watching 300+ developers walk into Dedan Kimathi University, I had this weird mix of pride and pure terror.
Two years ago, I was just another confused student at DevFest 2023, try...]]></description><link>https://blog.lxmwaniky.me/devfest-mt-kenya-2025-insights</link><guid isPermaLink="true">https://blog.lxmwaniky.me/devfest-mt-kenya-2025-insights</guid><category><![CDATA[devfest]]></category><category><![CDATA[Tech community]]></category><category><![CDATA[TechEvents]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 09 Nov 2025 20:24:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762716812421/9ac38333-bed1-4c2e-b22d-7f84dab42b8f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Look, I'm not gonna lie. Standing backstage at DevFest Mt. Kenya 2025, watching 300+ developers walk into Dedan Kimathi University, I had this weird mix of pride and pure terror.</p>
<p>Two years ago, I was just another confused student at DevFest 2023, trying to figure out what the hell everyone was talking about. I didn't know what APIs were. I definitely didn't know what Kubernetes was. Hell, I was just there because someone said there'd be free food and maybe I'd meet people who could explain why my code kept breaking.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762717025064/03558076-5b82-44d3-9a18-cda006b6f4d7.jpeg" alt="DevFest Mt Kenya 2023" class="image--center mx-auto" /></p>
<p>Fast forward to November 8th, 2025. Same event. Different role. This time, I wasn't just attending. I was organizing it. And speaking at it. About Google Kubernetes Engine. To 300+ people.</p>
<p>How the hell did we get here?</p>
<h2 id="heading-the-random-idea-that-started-it-all">The Random Idea That Started It All</h2>
<p>DevFest Mt. Kenya almost didn't happen this year. The usual leads weren't available, and for a hot minute, it looked like the Mt. Kenya region would just... skip it.</p>
<p><a target="_blank" href="https://www.linkedin.com/in/eleanora-matalanga-49ba7a1bb/">Eleanora</a> and I were at the Google Offices when she broke the news, and we looked at each other like, "Nah. Hell no. We're doing this ourselves."</p>
<p>It was one of those stupid ideas that sound easy until you actually start planning. You know, the kind where you're like "how hard can it be?" and then reality hits you at 2 AM during your fifth planning meeting that week.</p>
<p>But we did it anyway. Because sometimes you just gotta be dumb enough to try.</p>
<h2 id="heading-organizing-devfest-a-masterclass-in-controlled-chaos">Organizing DevFest: A Masterclass in Controlled Chaos</h2>
<p>Organizing DevFest Mt. Kenya 2025 taught me more about event management than any course ever could.</p>
<p>We collaborated with campuses across the Mt. Kenya region. We had late-night meetings where we debated everything from sponsorship decks to whether we should order chicken or beef for lunch.</p>
<p>We dealt with budget constraints, last-minute speaker changes etc. That shit kept me up at night.</p>
<p>Seeing that crowd walk in? That was the moment I realized we actually pulled this off. All those sleepless nights, all those "are we sure this is gonna work?" conversations, they paid off.</p>
<p>And I couldn't have done it alone. Not even close. The organizing team, the volunteers, the partners who believed in this vision - they made it happen. This wasn't my event. It was ours.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762718107976/b9242d54-9f82-47fc-931f-2febb754cf16.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-standing-on-stage-and-trying-not-to-pass-out">Standing on Stage (And Trying Not to Pass Out)</h2>
<p>Let's talk about my GKE talk.</p>
<p>I've given presentations before. In class. To maybe 30 people max. Always with the safety net of knowing everyone in the room.</p>
<p>This was different.</p>
<p>In a conference hall. With a microphone. And expectations.</p>
<p>Imposter syndrome hit me backstage. My brain was doing that thing where it replays every possible way you can mess up:</p>
<p><em>"What if the slides don't load?"</em> <em>"What if nobody understands what I'm saying?"</em> <em>"What if I forget everything mid-sentence?"</em> <em>"What if they realize I'm just a student who Googles half his problems?"</em></p>
<p>Stage fright is real, man. I don't care how confident you look on the outside - standing in front of hundreds of people who expect you to teach them something is scary as hell.</p>
<p>But then I started talking. About containers. About Kubernetes. About why orchestration matters in cloud-native applications. And somehow, people were nodding, taking notes. Actually engaged.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762718353916/b22d94b7-3100-4190-a5fc-ed085c8fe5e3.jpeg" alt class="image--center mx-auto" /></p>
<p>I realized something mid-talk: I've been teaching myself this shit for two years. I've broken production deployments. I've debugged at 3 AM. I've read the docs until my eyes hurt. I might not be an expert, but I know enough to help someone else get started.</p>
<p>And that's enough.</p>
<p>The Q&amp;A after the talk? Even better. People asked real questions and also "gotcha" questions to test me, about how to deploy their own apps, how to choose between services, how to get started with cloud computing.</p>
<p>That's when I knew the talk worked. Not because I was perfect, but because I made something complicated feel approachable.</p>
<p>You can read more about <a target="_blank" href="https://blog.lxmwaniky.me/conducting-my-first-cloud-symphony">my GKE talk here</a></p>
<p>After my talk, a few people came up to me. Some wanted clarifications on Kubernetes concepts. Others just wanted to correct me(I’m not always right).</p>
<p>But one conversation stuck with me.</p>
<p>A student from another campus asked, "How do you grow a tech community? We don't have one at our school, and I want to start something, but I don't know where to begin."</p>
<p>I swear, it felt like looking at myself two years ago.</p>
<p>I remember standing at DevFest 2023, asking the exact same question. Feeling like everyone else had it figured out except me. Wondering if I even had what it takes to lead anything.</p>
<p>And now here I was, on the other side of that conversation, giving advice. Telling them what worked for us. Encouraging them to just start, even if it feels small.</p>
<p>It's wild how life circles back like that.</p>
<h2 id="heading-the-uncomfortable-parts-nobody-talks-about">The Uncomfortable Parts Nobody Talks About</h2>
<p>Real talk for a second: MCing isn't my thing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762718549969/2d46f93c-5d4e-4cae-9113-503caaa48aee.jpeg" alt class="image--center mx-auto" /></p>
<p>I'm cool with teaching. I can stand on a stage and explain Kubernetes for an hour. But walking around, making small talk with unfamiliar faces, keeping energy high between sessions? That drains me.</p>
<p>I'm somehow uncomfortable with strangers even though I'm literally organizing events for hundreds of people. Make it make sense.</p>
<p>Also, if you took a selfie with me during DevFest, please share those pics. I need proof this shit actually happened.</p>
<h2 id="heading-what-devfest-taught-me">What DevFest Taught Me</h2>
<p>Organizing DevFest Mt. Kenya 2025 wasn't just about pulling off a successful event. It taught me things I didn't expect to learn:</p>
<p><strong>1. You don't need to be perfect to lead.</strong></p>
<p>I'm not the smartest person in the room. I'm not the most experienced. But I showed up. I put in the work. I asked for help when I needed it. That's enough.</p>
<p><strong>2. Communities are built by people who care, not by people who know everything.</strong></p>
<p>Half the organizing team were students. We made mistakes. We improvised solutions on the fly. But we cared about creating something valuable for our region, and that mattered more than having all the answers.</p>
<p><strong>3. Imposter syndrome doesn't go away. You just learn to do it scared.</strong></p>
<p>I still feel like a fraud sometimes. Like someone's gonna tap me on the shoulder and say "You're not qualified for this." But you do the thing anyway. The fear doesn't stop you from showing up.</p>
<p><strong>4. The people you meet make it worth it.</strong></p>
<p>I met developers who are building amazing projects. Students who are hungry as hell to learn. Professionals who took time to mentor and guide. People who challenged me to keep growing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762718638809/64660b25-e27f-4910-ab4a-bff811096057.jpeg" alt class="image--center mx-auto" /></p>
<p>That's what makes communities powerful - not the events, but the connections you build.</p>
<h2 id="heading-whats-next">What's Next?</h2>
<p>Honestly? I don't fully know.</p>
<p>I want to keep speaking. DevFest 2025 was my first physical talk at an event, and despite the nerves, I'm looking forward to more. There's something about teaching that feels right, you know? Like this is what I'm supposed to be doing.</p>
<p>I'm also working towards becoming a Google Developer Expert someday. Sounds ambitious as hell, but why not? Two years ago, I didn't think I'd be organizing DevFests or speaking about cloud infrastructure. If that can happen, anything can.</p>
<p>Maybe next time, we'll take the University of Embu community to DevFest Nairobi or DevFest Pwani. Experience the energy of bigger conferences. Learn from other communities. Bring that knowledge back home.</p>
<p>Because that's what it's all about - lifting as you climb. Learning and teaching at the same time. Building something bigger than yourself.</p>
<h2 id="heading-to-everyone-who-made-this-possible">To Everyone Who Made This Possible</h2>
<p>This event happened because of us.</p>
<p>To the organizing team, the volunteers, the partners who believed in this vision, the speakers who shared their knowledge, and the 300+ attendees who showed up - thank you.</p>
<p>You reminded me why I fell in love with tech communities in the first place.</p>
<p>It's not about the code. It's not even about the technology.</p>
<p>It's about the people. It always has been.</p>
<p>If you're reading this and you're thinking about starting something - a community, an event, a project - just do it. It'll be scary. You'll doubt yourself. You'll wonder if you're qualified.</p>
<p>But you don't need permission to start. You just need to show up.</p>
<p>And if a confused kid from Embu who Googled "what is an API" two years ago can organize a 300+ person developer conference and speak about Kubernetes, trust me - you can do whatever the hell you're dreaming about too.</p>
<p>Just start. Others will follow.</p>
]]></content:encoded></item><item><title><![CDATA[Test-Driven Development: Write Better Code by Writing Tests First]]></title><description><![CDATA[What is TDD Really?
Test-Driven Development (TDD) is a coding approach where you write tests before writing the actual code. It sounds backward, but it transforms how you design and build software.
The cycle is simple:

RED - Write a failing test

GR...]]></description><link>https://blog.lxmwaniky.me/test-driven-development</link><guid isPermaLink="true">https://blog.lxmwaniky.me/test-driven-development</guid><category><![CDATA[software development]]></category><category><![CDATA[Python]]></category><category><![CDATA[TDD (Test-driven development)]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Tue, 21 Oct 2025 23:45:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761090874323/b0d26802-c802-4580-ba38-86d4b51dc356.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-tdd-really">What is TDD Really?</h2>
<p><a target="_blank" href="https://www.geeksforgeeks.org/software-engineering/test-driven-development-tdd/">Test-Driven Development</a> (TDD) is a coding approach where you <strong>write tests before writing the actual code</strong>. It sounds backward, but it transforms how you design and build software.</p>
<p>The cycle is simple:</p>
<ol>
<li><p><strong>RED</strong> - Write a failing test</p>
</li>
<li><p><strong>GREEN</strong> - Write minimal code to pass</p>
</li>
<li><p><strong>REFACTOR</strong> - Improve code without breaking tests</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761089863280/32d3b705-b3a9-473c-9333-c3ba8d684ad5.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-why-bother-with-tdd">Why Bother with TDD?</h2>
<h3 id="heading-youll-write-code-that-actually-works">You'll Write Code That Actually Works</h3>
<p>How many times have you "finished" coding, only to find hidden bugs later? TDD ensures every piece of functionality is verified the moment it's written.</p>
<h3 id="heading-it-prevents-over-engineering">It Prevents Over-Engineering</h3>
<p>When you write tests first, you only build what you actually need. No more "what if" features that complicate your code.</p>
<h3 id="heading-your-tests-actually-test-something">Your Tests Actually Test Something</h3>
<p>Ever written tests that always pass? With TDD, you see tests fail first, proving they can catch real problems.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761089896482/2725598c-06fc-47d4-bcd0-ccf2ea809938.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-lets-build-twitters-vowel-remover">Let's Build Twitter's Vowel Remover</h2>
<p>Twitter famously removed vowels to fit messages in 140 characters. "Twitter" becomes "twttr". Let's build this using TDD.</p>
<h3 id="heading-step-1-red-write-a-failing-test">Step 1: RED - Write a Failing Test</h3>
<pre><code class="lang-python"><span class="hljs-comment"># test_twttr.py</span>
<span class="hljs-keyword">from</span> twttr <span class="hljs-keyword">import</span> shorten

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_remove_vowels_from_twitter</span>():</span>
    result = shorten(<span class="hljs-string">"twitter"</span>)
    <span class="hljs-keyword">assert</span> result == <span class="hljs-string">"twttr"</span>
</code></pre>
<p>Run this test (<code>pytest test_</code><a target="_blank" href="http://twttr.py"><code>twttr.py</code></a>). It fails because <code>shorten()</code> it doesn't exist yet.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761089987022/de10a8f9-2af8-46cd-b573-8bf787b26462.png" alt class="image--center mx-auto" /></p>
<p><strong>Why this matters</strong>: The failure proves our test works and can detect problems.</p>
<h3 id="heading-step-2-green-write-minimal-code-to-pass">Step 2: GREEN - Write Minimal Code to Pass</h3>
<pre><code class="lang-python"><span class="hljs-comment"># twttr.py</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">shorten</span>(<span class="hljs-params">word</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"twttr"</span>  <span class="hljs-comment"># The simplest thing that could work(Cheating)</span>
</code></pre>
<p>Run the test again. It passes!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761090063000/f2b2c27e-1acd-47c5-8572-3ac794de6ad0.png" alt class="image--center mx-auto" /></p>
<p><strong>Why we "cheat"</strong>: This keeps us focused on one requirement at a time.</p>
<h3 id="heading-step-3-add-another-test-back-to-red">Step 3: Add Another Test - Back to RED</h3>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_remove_vowels_from_hello</span>():</span>
    result = shorten(<span class="hljs-string">"hello"</span>)
    <span class="hljs-keyword">assert</span> result == <span class="hljs-string">"hll"</span>
</code></pre>
<p>Run tests again. First test passes, second fails. Perfect progression.</p>
<h3 id="heading-step-4-write-real-logic-green">Step 4: Write Real Logic - GREEN</h3>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">shorten</span>(<span class="hljs-params">word</span>):</span>
    result = <span class="hljs-string">""</span>
    <span class="hljs-keyword">for</span> char <span class="hljs-keyword">in</span> word:
        <span class="hljs-keyword">if</span> char.lower() <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> <span class="hljs-string">"aeiou"</span>:
            result += char
    <span class="hljs-keyword">return</span> result
</code></pre>
<p>Both tests pass! We now have working logic.</p>
<h3 id="heading-step-5-add-edge-cases">Step 5: Add Edge Cases</h3>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_empty_string</span>():</span>
    <span class="hljs-keyword">assert</span> shorten(<span class="hljs-string">""</span>) == <span class="hljs-string">""</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_all_vowels</span>():</span>
    <span class="hljs-keyword">assert</span> shorten(<span class="hljs-string">"aeiou"</span>) == <span class="hljs-string">""</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_mixed_case</span>():</span>
    <span class="hljs-keyword">assert</span> shorten(<span class="hljs-string">"Hello World"</span>) == <span class="hljs-string">"Hll Wrld"</span>
</code></pre>
<p>All tests pass with our current implementation.</p>
<h3 id="heading-step-6-refactor">Step 6: REFACTOR</h3>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">shorten</span>(<span class="hljs-params">word</span>):</span>
    vowels = <span class="hljs-string">"aeiouAEIOU"</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>.join(char <span class="hljs-keyword">for</span> char <span class="hljs-keyword">in</span> word <span class="hljs-keyword">if</span> char <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> vowels)
</code></pre>
<p>Run tests again - still green! We improved the code without breaking anything.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761090112182/808125d6-158e-44a4-b184-7f4e17760530.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-common-tdd-objections-and-why-theyre-wrong"><strong>Common TDD Objections (And Why They're Wrong)</strong></h2>
<h3 id="heading-it-slows-me-down">"It Slows Me Down"</h3>
<p>Short-term: yes. Long-term: you save hours of debugging and rewriting.</p>
<h3 id="heading-my-code-changes-too-much-for-tests">"My Code Changes Too Much for Tests"</h3>
<p>If your code changes frequently, tests are even more valuable. They catch breaks immediately.</p>
<h3 id="heading-i-dont-know-what-to-test">"I Don't Know What to Test"</h3>
<p>Start with the happy path, then add edge cases:</p>
<ul>
<li><p>Normal input</p>
</li>
<li><p>Empty input</p>
</li>
<li><p>Boundary cases</p>
</li>
<li><p>Error conditions</p>
</li>
</ul>
<h2 id="heading-when-tdd-shines">When TDD Shines</h2>
<ul>
<li><p><strong>New features</strong>: Design through tests</p>
</li>
<li><p><strong>Bug fixes</strong>: Reproduce the bug with a test first</p>
</li>
<li><p><strong>Legacy code</strong>: Add tests before modifying</p>
</li>
<li><p><strong>Learning</strong>: Understand requirements before implementing</p>
</li>
</ul>
<h2 id="heading-your-tdd-cheat-sheet">Your TDD Cheat Sheet</h2>
<ol>
<li><p><strong>Think</strong>: What's the smallest behaviour I need?</p>
</li>
<li><p><strong>Test</strong>: Write a test for that behaviour</p>
</li>
<li><p><strong>Run</strong>: Watch it fail (RED)</p>
</li>
<li><p><strong>Code</strong>: Write minimal code to pass (GREEN)</p>
</li>
<li><p><strong>Clean</strong>: Improve code without breaking tests (REFACTOR)</p>
</li>
<li><p><strong>Repeat</strong>: Choose the next smallest behaviour</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761090150501/a177f0ff-8f77-4f3a-a017-4492c040f05a.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-bottom-line">Bottom Line</h2>
<p>TDD isn't about religiously following rules. It's about building confidence in your code, one verified piece at a time.</p>
<p>The next time you start coding, try writing the test first. That red-to-green transition might just become your new addiction.</p>
]]></content:encoded></item><item><title><![CDATA[Reflections on My Year as GDG on Campus Lead]]></title><description><![CDATA[As I look at my Certificate of Appreciation from Google Developer Groups, I'm reminded that leadership isn't just about titles—it's about transformation. Both for the community you serve and for yourself.

The Challenge of Starting
When I first stepp...]]></description><link>https://blog.lxmwaniky.me/reflections-on-my-year-as-gdg-on-campus-lead</link><guid isPermaLink="true">https://blog.lxmwaniky.me/reflections-on-my-year-as-gdg-on-campus-lead</guid><category><![CDATA[GDG]]></category><category><![CDATA[Google]]></category><category><![CDATA[Google Developers Group]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sat, 13 Sep 2025 07:05:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757746790433/c4da235f-cbbc-405f-973d-c4a678199554.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As I look at my Certificate of Appreciation from Google Developer Groups, I'm reminded that leadership isn't just about titles—it's about transformation. Both for the community you serve and for yourself.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757747066120/c02cd354-46bf-48ff-b91f-68062a71ba1e.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-challenge-of-starting">The Challenge of Starting</h2>
<p>When I first stepped into the role of GDG on Campus Lead at the University of Embu, I thought I knew what I was getting into. Organise some tech talks, maybe a few workshops, and connect students with Google technologies. Simple enough, right?</p>
<p>I couldn't have been more wrong.</p>
<p>The reality hit me during our first event planning meeting. Staring at a room of eager students with diverse skill levels, different interests, and varying degrees of confidence, I realised that my job wasn't just about Google technologies—it was about people. It was about creating an environment where a first-year computer science student felt comfortable asking questions alongside a final-year developer building their capstone project.</p>
<h2 id="heading-the-growth-in-complexity">The Growth in Complexity</h2>
<p>What started as "let's do some Flutter workshops" evolved into something much richer. We found ourselves navigating questions that went beyond code:</p>
<ul>
<li><p>How do you make technical content accessible to students who are still finding their footing in programming?</p>
</li>
<li><p>How do you balance structured learning with the organic curiosity that drives real innovation?</p>
</li>
<li><p>How do you build confidence in students who see technology as intimidating rather than empowering?</p>
</li>
</ul>
<p>Each event taught me that technical leadership is deeply human work. The best workshops weren't just about Firebase or Flutter—they were about creating moments where students realised they could build things that mattered.</p>
<h2 id="heading-the-unexpected-lessons">The Unexpected Lessons</h2>
<p><strong>Leadership is listening.</strong> The most impactful changes we made came from really hearing what our community needed. Sometimes that meant pivoting from a planned AI workshop to a basic Git tutorial because that's where the real knowledge gap was.</p>
<p><strong>Consistency beats intensity.</strong> The students who benefited most weren't necessarily those who attended our biggest events, but those who engaged regularly with our smaller, ongoing initiatives.</p>
<p><strong>Your role is to make yourself unnecessary.</strong> By the end of the year, seeing other students step up to lead sessions and mentor their peers was more rewarding than any certificate.</p>
<h2 id="heading-the-technical-and-the-personal">The Technical and the Personal</h2>
<p>This role taught me to bridge worlds—translating complex technical concepts into engaging learning experiences, connecting academic theory with practical application, and most importantly, helping students see themselves not just as consumers of technology, but as creators.</p>
<p>The Google Developer ecosystem provided incredible resources, but the real magic happened in the spaces between the official curriculum.</p>
<h2 id="heading-looking-forward">Looking Forward</h2>
<p>As I transition from this role, I'm carrying forward a deeper understanding of what the developer community means. It's not just about the latest frameworks or trending technologies—it's about fostering environments where curiosity thrives, where failure is reframed as learning, and where everyone has the support they need to grow.</p>
<p>To future GDG on Campus Leads: embrace the complexity. Your role is part educator, part community builder, part mentor, and part student. The technical skills you'll develop are valuable, but the leadership skills you'll gain are transformative.</p>
<p>The developer community needs leaders who understand that our most important work happens not in the code we write, but in the opportunities we create for others to discover what they're capable of building.</p>
<h2 id="heading-gratitude-and-recognition">Gratitude and Recognition</h2>
<p>This journey wouldn't have been possible without an incredible support system:</p>
<p><em>My Core Team</em></p>
<p><em>Google Developer Experts (GDEs)</em></p>
<p><em>Regional Community Managers</em></p>
<p><em>University of Embu</em></p>
<p><em>Community Contributors</em></p>
<p>Leadership in tech communities is never a solo effort—it's the result of an ecosystem of support, mentorship, and collaboration.</p>
<hr />
<p><em>Thank you to Google Developer Groups and the entire community that made this year of growth and impact possible.</em></p>
]]></content:encoded></item><item><title><![CDATA[Coding Didn’t Change My Life — Communities Did]]></title><description><![CDATA[Communities have been everything to me in tech. I'm not exaggerating when I say that without them, I'd probably still be that confused first-year student who thought "hacking" was just typing fast on a keyboard. They've pushed me, supported me, frust...]]></description><link>https://blog.lxmwaniky.me/coding-didnt-change-my-life-communities-did</link><guid isPermaLink="true">https://blog.lxmwaniky.me/coding-didnt-change-my-life-communities-did</guid><category><![CDATA[GDG]]></category><category><![CDATA[tech ]]></category><category><![CDATA[leadership]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Wed, 10 Sep 2025 23:45:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1757545979801/4c49d7b0-ea3f-45d0-821c-b236c056b147.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Communities have been everything to me in tech. I'm not exaggerating when I say that without them, I'd probably still be that confused first-year student who thought "hacking" was just typing fast on a keyboard. They've pushed me, supported me, frustrated me, and ultimately shaped me into someone I never thought I could become. This is the story of how I went from being completely lost at a tech event to leading the very communities that once intimidated the hell out of me.</p>
<p>Let me start from the beginning, because every good story needs context for you to understand just how far this journey has taken me.</p>
<h2 id="heading-the-day-reality-hit-me-hard">The Day Reality Hit Me Hard</h2>
<p>November 2023. I still get goosebumps thinking about that day when I organised transport for 10 students from my campus to DevFest Mt Kenya at DEKUT. It was literally the first thing I had ever organised in my life, and looking back, I had no business being there. I was this naive first-year student who genuinely believed I was destined to become some kind of elite hacker. Yeah, I know how that sounds now, but at 18, fresh out of high school, I thought I had the tech world figured out.</p>
<p>I held this title, "Cyber Security Lead" at our campus GDSC chapter, which sounds impressive until you realise I had never done a single thing related to cybersecurity. Not one workshop, not one session, nothing. I was living in this bubble where having a title meant I actually knew what I was doing. The reputation I had built at school felt solid, and other students would come to me with tech questions like I was some kind of expert.</p>
<p>Then DevFest happened.</p>
<p>Sitting in that auditorium at DEKUT, listening to developers casually throw around terms I couldn't even spell, let alone understand, was humbling in the worst possible way. These people were talking about APIs, cloud architecture, and machine learning models, and I'm sitting there wondering if "Postman" was actually a person who delivered mail. The presentations might as well have been in a foreign language. Every session made me realise how little I actually knew, how fake my confidence had been.</p>
<p>The ride back to Embu was probably the longest two hours of my life. While other students were excitedly discussing what they'd learned, I was having an internal crisis. All that reputation I'd built? It felt like a house of cards that had just collapsed. I felt like a fraud, and worse, I felt completely lost about what to do next.</p>
<h2 id="heading-the-postman-obsession-that-changed-everything">The Postman Obsession That Changed Everything</h2>
<p>But here's the thing about rock bottom - it gives you a solid foundation to build from. Throughout that entire DevFest event, one thing kept nagging at me. Everyone kept mentioning "Postman" as the event’s sponsor, and they talked about it like it was this amazing tool for API testing. I had no clue what APIs even were, but something about the way people talked about Postman made it sound accessible, even fun.</p>
<p>After the event, I couldn't get it out of my head. I kept thinking about this tool and wondering what I was missing. Then I remembered something - Brian Muchai, the Postman Student Leader at DEKUT, looked familiar. It took me a while to place him, but then it hit me - we went to the same high school! This felt like fate, or at least like the universe giving me a small break.</p>
<p>Reaching out to Brian was terrifying. Here I was, this guy who had just realised he knew nothing about tech, asking someone who seemed to have it all figured out for help. But Brian was incredible. Instead of making me feel stupid for not knowing basic concepts, he actually encouraged me to apply as a Postman Student Leader for my own campus. He told me about the API 101 course and how it could help me understand the fundamentals.</p>
<p>I signed up for that virtual course immediately, and for the first time in months, things started making sense. APIs weren't this mysterious, impossible concept - they were actually logical and useful. The course was structured in a way that assumed you knew nothing, which was perfect for someone like me who literally knew nothing. I spent hours going through the modules, taking notes, and practicing with the Postman interface.</p>
<p>When it came time to hold my first session as part of the application process, I was absolutely terrified. My hands were shaking so badly I could barely hold my laptop steady. I was about to teach API concepts to other students when I'd only learned them myself a few days earlier. But you know what happened? The session was amazing. Students were asking questions, getting excited about building their first API requests, and I could see that same spark of understanding in their eyes that I'd felt when things first clicked for me.</p>
<p>Writing that application report was nerve-wracking, but I poured everything into it - my newfound understanding of APIs, my vision for bringing practical tech skills to our campus, and my commitment to helping other students avoid the confusion I'd experienced. When the acceptance email came through, I literally jumped out of my chair. I was officially a Postman Student Leader, and it felt like my first real achievement in tech.</p>
<h2 id="heading-watching-gdsc-slowly-die-and-deciding-to-save-it">Watching GDSC Slowly Die (And Deciding to Save It)</h2>
<p>By this time, I was juggling two roles - my new position as a Postman Student Leader and my existing spot on the GDSC core team as the Cyber Security lead (still not doing any actual cybersecurity work, but at least now I was contributing in other ways). Being part of both communities gave me a unique perspective on what was working and what wasn't.</p>
<p>The contrast was stark. Through Postman, I was learning practical skills that I could actually use and teach to others. The content was relevant, the community was supportive, and students were genuinely engaged. But GDSC on our campus was struggling. We weren't inactive exactly, but there was this sense that we were just going through the motions.</p>
<p>The problem became crystal clear as the academic year progressed - almost all of our GDSC leads were final year students. While I was getting excited about APIs and community building, they were focused on graduation, job applications, and moving on with their lives. I don't blame them - that's the natural progression. But as someone who was just finding his footing in tech, the thought of losing this community was devastating.</p>
<p>I started noticing things that other people missed. Leads would skip meetings because they had commitments. Events were getting smaller because there wasn't consistent promotion. New students weren't joining because the energy just wasn't there. We were slowly bleeding out, and nobody seemed to care because everyone was already mentally checked out.</p>
<p>That's when it hit me - someone needed to step up, and that someone might have to be me. The thought was terrifying. I was barely keeping up with my own learning, still figuring out basic concepts, and now I was considering taking responsibility for an entire community? But the alternative was watching GDSC die completely, and I couldn't let that happen. Not when I'd seen firsthand through Postman how powerful tech communities could be.</p>
<h2 id="heading-building-a-team-before-i-was-even-the-leader">Building a Team Before I Was Even the Leader</h2>
<p>March 2024 was when I made one of the boldest decisions of my life. Instead of waiting for the official GDSC lead selection process in September, I decided we needed to start building a new core team immediately. The math was simple - if we waited until September, the current leads would already be gone, and GDSC would be left in limbo for months. We couldn't afford that kind of gap.</p>
<p>I wasn't even officially the lead yet, but I was already thinking like one. The selection process was intense, but not in the way you might expect. I wasn't looking for the students with the highest GPAs or the most impressive GitHub profiles. I was looking for something much more valuable - passion. I wanted people who got excited when they talked about helping others learn, who asked thoughtful questions during our sessions, and who understood that building a community meant we'd all be learning together.</p>
<p>I remember interviewing potential team members and asking them why they wanted to be part of GDSC. Some gave these rehearsed answers about wanting to "enhance their technical skills" or "build their professional network." Those weren't necessarily wrong answers, but they weren't what I was looking for. The candidates who got selected were the ones who talked about wanting to create something meaningful, who shared stories about being confused by tech concepts and wanting to help others avoid that confusion, who understood that leadership in a student community meant being a facilitator, not a know-it-all.</p>
<p>The core team I built became the backbone of everything we accomplished. They became my support system, my sounding board, and the people who kept the community running when I was struggling with my own challenges. Working with this team taught me that the best communities are built by people who complement each other's strengths and cover for each other's weaknesses.</p>
<p>During this transition period, we started organizing Flutter Study Jams across campus, and these became our testing ground. We weren't just teaching Flutter - we were learning how to work as a team, how to coordinate logistics, how to keep students engaged week after week, and how to handle the inevitable technical difficulties that come with any live coding session.</p>
<p>I still remember the anxiety before our first Flutter Study Jam. We'd planned everything meticulously, but I kept wondering - what if nobody shows up? What if the Wi-Fi fails? What if I can't answer their questions? What if we're actually making things more confusing instead of helping? But students started trickling in, and then more came, and before we knew it, we had a room full of people genuinely excited to learn about mobile app development.</p>
<p>Watching students build their first Flutter apps, seeing them get excited when their code compiled successfully, answering their questions and realizing I actually knew the answers - these moments were addictive. Each session built our confidence as a team and proved that we could actually do this. By the time the official transition period came around in September, we weren't just hoping to keep GDSC alive - we had already proven we could make it thrive.</p>
<h2 id="heading-the-interview-that-changed-my-life">The Interview That Changed My Life</h2>
<p>April 2024 brought the interview for the GDSC Lead position, and I've never been more nervous about anything in my life. This wasn't just a casual conversation - this was my chance to officially take responsibility for something I'd already been working toward for months.</p>
<p>I remember sitting in front of my laptop for that virtual interview, trying to balance confidence with humility, explaining my vision for what GDSC could become on our campus. I talked about the Flutter Study Jams we'd already organized, the team I'd been building, and my experience as a Postman Student Leader. I shared my frustrations with the disconnect between what we learned in class and what the industry actually needed, and my belief that student communities could bridge that gap.</p>
<p>The interviewer asked me about challenges I anticipated, and I was honest about them - the difficulty of maintaining engagement, the challenge of teaching concepts I was still learning myself, and the pressure of living up to expectations. But I also talked about why I was willing to take on those challenges, about seeing students get excited about tech concepts, about the potential I saw in our campus community.</p>
<p>The waiting period between April and September felt endless. Every day I wondered if I'd said the right things, if I'd come across as confident enough, if they could tell how much this meant to me. When that acceptance email finally arrived, I read it three times before it sank in. I was officially going to be the GDSC Lead, and suddenly all those months of preparation felt real.</p>
<h2 id="heading-the-recruitment-revolution">The Recruitment Revolution</h2>
<p>September 2024 marked the beginning of what I like to call our "recruitment revolution," though that probably sounds more dramatic than it actually was. We had a new academic year starting, which meant hundreds of fresh first-year students who were curious, eager, and hadn't been discouraged by our campus's previously weak tech culture.</p>
<p>Our recruitment strategy was aggressive but welcoming. We didn't just put up posters and hope people would show up - we went to where the students were. We set up booths during orientation week, we visited first-year classes to give brief presentations, we organized "Introduction to Tech" sessions that were specifically designed for complete beginners. We wanted students to know that GDG on Campus(Structure changed) wasn't just for people who already knew how to code.</p>
<p>The response was incredible, but it also created new challenges. Suddenly we had students with wildly different skill levels all wanting to learn together. Some had never written a line of code, while others had been programming since high school. Some wanted to learn web development, others were interested in mobile apps, and a growing number were curious about AI and machine learning.</p>
<p>We decided to embrace this diversity instead of trying to segment everyone into different skill levels. We introduced new tech stacks that we'd never covered before - AI and ML became regular topics in our sessions, which was both exciting and terrifying because I was learning these concepts alongside everyone else. We organized Cloud Study Jams that introduced students to Google Cloud Platform, hosted Career Tech Talks where industry professionals shared real insights about working in tech, and ran workshops covering everything from basic programming concepts to advanced development frameworks.</p>
<p>The energy was infectious. Students were staying after sessions to ask questions, forming study groups on their own, collaborating on personal projects, and actually building things together. We weren't just a club anymore - we were becoming a real community where learning happened both during formal sessions and in the spaces between them.</p>
<h2 id="heading-devfest-2024-from-10-to-over-40">DevFest 2024: From 10 to Over 40</h2>
<p>When DevFest 2024 was announced, I knew this would be the ultimate test of how far we'd come. The previous year, I'd struggled to convince 10 people to attend. This year, we were looking at taking over 40 students, and they weren't just coming for the experience - they were coming because they were genuinely excited about what they might learn.</p>
<p>The logistics were a complete nightmare. Coordinating transport for that many people, managing registrations, collecting payments, making sure everyone knew what to expect, handling last-minute cancellations and additions - it was like running a small travel agency. But seeing that bus (yes, we could finally afford to rent an entire bus!) filled with excited students from our campus was one of the most rewarding moments of my entire journey.</p>
<p>The difference in our students' experience compared to my first DevFest was night and day. They weren't sitting there confused and overwhelmed - they were engaged, asking intelligent questions, taking notes, networking with other attendees, and actually understanding the presentations. During the lunch breaks, I watched our community members confidently approaching speakers to ask follow-up questions, exchanging contacts with students from other universities, and discussing project ideas with each other.</p>
<p>When we got back to campus, the energy didn't die down. Students were sharing what they'd learned, starting new projects inspired by what they'd seen, and recruiting their friends to join our community. By this time, we had grown to over 500 members, a number that still amazes me when I think about where we started. We weren't just a student club anymore - we were a movement, a family, a platform where students could discover their potential and actually do something meaningful with their skills.</p>
<h2 id="heading-making-it-official-registration-and-real-impact">Making It Official: Registration and Real Impact</h2>
<p>One of the most important victories we achieved was getting GDSC officially registered with the university by September 2024. This might sound like boring administrative work, but it was actually a game-changer for everything we wanted to accomplish.</p>
<p>Before registration, we were essentially operating in the shadows. We couldn't officially book large venues for events, we couldn't access funding opportunities that were available to registered student organisations, we couldn't rent transport for trips, and we missed out on partnerships with external organisations that only worked with officially recognised groups.</p>
<p>The registration process itself was a masterclass in bureaucracy and patience. There were forms to fill out, constitutions to draft, faculty advisors to recruit, and endless back-and-forth with the student affairs office. But when we finally got that official approval, it opened doors we didn't even know existed.</p>
<p>This official status allowed us to host what became the first-ever student-led tech events at our campus - something that still makes me incredibly proud. We organised the ICP Hubs Kenya Campus Tour, bringing blockchain and Web3 education directly to our students. This wasn't just a one-hour presentation - it was a full-day event with hands-on workshops, networking opportunities, and real industry professionals sharing their expertise.</p>
<p>We also partnered with Moringa School to host a comprehensive Data Science and Cyber Security Workshop. As a Moringa Campus Ambassador (yes, I had started accumulating these titles), I was able to leverage that relationship to bring industry-standard training right to our doorstep. These weren't small club meetings held in empty classrooms - these were legitimate events with proper venues, professional speakers, and students from across different departments attending.</p>
<p>The impact of these events went beyond just the immediate learning outcomes. They showed our entire campus that students could organize meaningful, professional-quality events. They proved that there was genuine interest in practical tech skills. And they established GDSC as a serious organization that other groups and institutions wanted to partner with.</p>
<h2 id="heading-the-hidden-battles-nobody-saw">The Hidden Battles Nobody Saw</h2>
<p>But behind all these successes, I was fighting battles that nobody could see, and this is the part of the story that I think is most important to share, especially for other student leaders who might be going through similar struggles.</p>
<p>Being a leader meant that people looked up to me, expected me to have answers, and assumed I had everything figured out. The reality was completely different. I was juggling my community responsibilities with a demanding ALX Software Engineering course that required hours of study every day. I was trying to maintain my academic performance, manage my personal relationships, and deal with family issues that would have been challenging for anyone, let alone a student trying to build a tech community from scratch.</p>
<p>Depression started creeping in during the quiet moments between events, when the adrenaline of organising wore off and I was left alone with the weight of everyone's expectations. There were nights when I'd sit in front of my laptop, knowing I had sessions to plan, reports to write, and team members depending on me, but feeling empty inside. The motivation just wasn't there, and the guilt of not being productive made everything worse.</p>
<p>Burnout became my constant companion. I'd wake up exhausted, drag myself through classes, force enthusiasm during sessions, and then collapse at the end of the day, only to repeat the cycle the next day. There were weeks when I considered quitting everything - Community, ALX, sometimes even university itself. The pressure felt overwhelming, and I wasn't sure I was strong enough to handle it all.</p>
<p>Imposter syndrome whispered in my ear constantly. When I was teaching concepts I'd learned just days before, when I was networking with industry professionals who seemed infinitely more knowledgeable, when students asked me questions I couldn't answer immediately - all of these moments made me feel like a fraud who would eventually be exposed.</p>
<p>The hardest part was that I couldn't show this vulnerable side to anyone. My team needed me to be strong and decisive. The community looked to me for direction and inspiration. My family expected me to be succeeding. Admitting my struggles felt like admitting failure, so I learned to hide behind a mask of competence and enthusiasm.</p>
<p>There were moments when that mask almost slipped completely. During particularly stressful periods, I'd find myself snapping at team members, making poor decisions because I was too tired to think clearly, or cancelling important meetings because I just couldn't handle one more responsibility. The people closest to me probably noticed that something was off, but I was too proud to ask for help.</p>
<h2 id="heading-learning-to-build-in-public">Learning to Build in Public</h2>
<p>Through all of this struggle, I learned one of the most valuable lessons of my entire journey: never build in the dark. This lesson came from the tech community itself, and it was revolutionary for someone who used to hide projects until they were "perfect."</p>
<p>Before joining tech communities, I had this mindset that everything needed to be polished and professional before sharing it with others. I'd work on projects alone, never showing them to anyone until I thought they were ready for judgment. This meant that most projects never got shared at all, because they never felt ready enough.</p>
<p>The tech community taught me a completely different approach. People shared their ugly code, their half-finished projects, their failed experiments, and their learning processes. Instead of being judged for these imperfections, they received helpful feedback, encouragement, and collaboration opportunities.</p>
<p>I started applying this principle to our GDSC community. Instead of only promoting events after they were perfectly planned, I'd share our planning process, ask for input from community members, and be transparent about challenges we were facing. Instead of only showcasing successful projects, we created spaces for people to share their work in progress, their learning struggles, and their experimental ideas.</p>
<p>This transparency transformed our community culture. Students stopped being afraid to ask questions because they saw that everyone was learning together. People started collaborating more because they could see where others needed help. Our events became better because we incorporated feedback during the planning process instead of only after the fact.</p>
<p>On a personal level, this principle helped me deal with my imposter syndrome. Instead of pretending I had all the answers, I started being honest about what I was learning alongside everyone else. Instead of hiding my struggles, I began sharing appropriate parts of my journey, which helped other students realize they weren't alone in their challenges.</p>
<h2 id="heading-the-google-recognition">The Google Recognition</h2>
<p>When I applied to be a GDG on Campus Organiser in early 2024, it felt like a long shot. Here I was, a first-year student who had only been seriously involved in tech communities for about a year, applying for a position that would make me an official representative of Google's developer community program at the campus.</p>
<p>The application process was thorough - they wanted to know about my experience leading communities, my vision for tech education on campus, my plans for collaboration with local GDGs, and my commitment to diversity and inclusion in tech. I spent weeks crafting my responses, trying to balance honesty about my relatively short experience with confidence about what I could accomplish.</p>
<p>The waiting period was agonising. Months passed without any communication, and I started assuming I hadn't been selected. I continued with my GDSC leadership, but in the back of my mind, I kept wondering what I could have done differently in my application.</p>
<p>When the acceptance email finally came in August 2025, I had to read it several times before it sank in. Google had selected me to be a GDG on Campus Organizer, which meant our transition from GDSC to GDGoC (Google Developer Groups on Campus). This wasn't just a name change or a bureaucratic shift - it represented recognition of the work we'd been doing and trust in our ability to continue growing the community.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757547409912/9f46c43d-ad40-4232-be92-1c4d4954fdba.png" alt class="image--center mx-auto" /></p>
<p>This transition also meant we'd be working more closely with local GDGs, sharing resources and expertise across different types of developer communities. It opened up new opportunities for our students to attend events beyond campus, to connect with working professionals in the tech industry, and to contribute to larger projects that extended beyond our university.</p>
<p>Looking back, this Google recognition felt like validation of a journey that had started with me feeling completely lost at DevFest 2023. In less than two years, I had gone from someone who didn't understand basic tech concepts to someone Google trusted to lead and build communities that would work hand in hand with their broader developer ecosystem.</p>
<h2 id="heading-the-mentorship-gap-that-shaped-my-leadership">The Mentorship Gap That Shaped My Leadership</h2>
<p>One area where my journey has been particularly difficult is mentorship - or rather, the complete lack of it. I've never had a proper mentor, and honestly, I've become somewhat resistant to the idea because of a painful experience that happened right after high school.</p>
<p>I had identified someone in the tech industry who I thought could guide me, and I reached out with genuine enthusiasm, asking for advice about getting started in technology. Instead of a polite decline or even constructive feedback, this person blocked me without explanation. That rejection left scars, making me hesitant to seek guidance from industry professionals even when I desperately needed it.</p>
<p>Instead of having someone to turn to for advice, I learned to be incredibly resourceful. I found answers through online communities, documentation, trial and error, and countless hours of self-directed learning. While this made me more independent and probably more creative in solving problems, I sometimes wonder how different my journey might have been with proper mentorship.</p>
<p>The lack of mentorship also meant that I made mistakes that could have been easily avoided, took longer paths to solutions that already existed, and struggled with decisions that an experienced mentor could have helped me navigate. There were times when I felt completely alone in my leadership role, unsure if the decisions I was making were right, with nobody to provide perspective or encouragement.</p>
<p>This experience has profoundly shaped how I approach mentorship within our community. Despite not having a mentor myself - or maybe because of it - I've tried to be the mentor figure I wish I'd had for the students in our community. I make myself available for questions, provide guidance and support, share my learning resources, and most importantly, I never make anyone feel stupid for asking basic questions or needing help with fundamental concepts.</p>
<p>I always tell aspiring leaders and students in our community to find mentors, but I also try to prepare them for the possibility of rejection. Don't let one bad experience close you off from the wisdom and guidance that could accelerate your growth, but also don't let the absence of formal mentorship stop you from pursuing your goals.</p>
<h2 id="heading-creating-a-platform-for-real-learning">Creating a Platform for Real Learning</h2>
<p>What I'm most proud of in this entire journey is the platform we created for students to learn skills that simply aren't taught in our regular classes. This wasn't just about adding more technical content to people's lives - it was about fundamentally changing how students on our campus thought about technology and their own potential.</p>
<p>When I first started at university, there was this massive gap between what we learned in lectures and what was actually happening in the tech industry. We'd study theoretical concepts in isolation, complete assignments that felt disconnected from real-world applications, and graduate without ever building anything meaningful or collaborative.</p>
<p>Through our community, we were able to bridge that gap. Students learned to work with APIs through our Postman sessions, built real mobile applications during Flutter Study Jams, deployed projects to the cloud through our Google Cloud workshops, and most importantly, learned to work together on projects that actually mattered to them.</p>
<p>But the learning went beyond just technical skills. Students learned to present their work confidently, to give and receive feedback constructively, to collaborate with people who had different skill levels and perspectives, and to persist through frustrating debugging sessions and failed experiments. These are the skills that actually matter in professional environments, and they're almost impossible to learn through traditional classroom instruction alone.</p>
<p>Our community also became a platform for students to showcase their work, connect with opportunities, and build genuine relationships. People found collaborators for hackathons, co-founders for startup ideas, mentors among older students, and friends who shared their interests and ambitions. The networking aspect happened naturally because it was built on shared learning experiences and mutual support rather than transactional exchanges.</p>
<p>We created an environment where it was safe to be a beginner, where asking questions was encouraged, where failure was treated as part of the learning process, and where everyone was expected to help others along the way. This community culture transformed not just individual students, but the entire tech ecosystem on our campus.</p>
<p>Looking around now, I see students who joined as complete beginners leading their own projects, organizing their own events, and mentoring newer members. I see projects that started in our workshops turning into viable products and even small businesses. I see alumni who credit their community experience with helping them land their first tech jobs or get accepted into competitive graduate programs.</p>
<h2 id="heading-where-this-journey-has-led-me">Where This Journey Has Led Me</h2>
<p>As I write this, reflecting on everything that's happened since that disappointing bus ride back from DevFest 2023, I'm amazed at how much can change in just two years. The confused first-year student who felt like he knew nothing about tech has become someone who leads communities, organizes large-scale events, partners with major organizations, and helps other students discover their own potential.</p>
<p>This journey has taken me into rooms I never thought I'd enter and connected me with people I never imagined I'd meet. I've presented at tech conferences, partnered with international organizations, been recognized by Google as a community leader, and most importantly, helped hundreds of students begin their own tech journeys with more support and direction than I had when I started.</p>
<p>But the real transformation isn't in the titles or recognitions - it's in how fundamentally my relationship with learning and leadership has changed. I no longer wait for permission to start building something meaningful. I no longer assume that someone else is better qualified to solve problems I care about. I no longer hide my work until it's perfect, and I no longer let fear of failure prevent me from trying new things.</p>
<p>The communities that once intimidated me have become the foundation of my growth, and now I have the opportunity to build communities that will hopefully do the same thing for other students who are where I was two years ago - confused, eager, and ready to learn if someone just shows them where to start.</p>
<p>The experience has taught me that leadership isn't about having all the answers - it's about being willing to ask the right questions and work toward solutions alongside other people who care about the same things. Community building isn't about creating perfect events and flawless programs - it's about creating spaces where people can learn, grow, and support each other through the messy, exciting process of discovering what they're capable of.</p>
<p>And perhaps most importantly, I've learned that the best way to overcome imposter syndrome isn't to become an expert at everything - it's to embrace the fact that we're all learning together, and that teaching others is one of the most effective ways to deepen your own understanding.</p>
<p>If you're reading this as someone who feels lost in tech, who doesn't think you belong in these communities, or who thinks you need to have everything figured out before you can contribute, I hope my story shows you that none of those things are true. Communities are built by people who care, not by people who already know everything. Your journey starts with showing up, asking questions, and being willing to learn alongside others who are on similar paths.</p>
<p>The tech world needs more people who are willing to build communities, support beginners, and create platforms for learning that go beyond what traditional education provides. It needs people who understand that the most valuable technical skills are often the ones we learn together, and that the most meaningful projects are the ones that help other people grow.</p>
<p>That's what communities have given me, and that's what I hope to give back through the communities I'm building now.</p>
]]></content:encoded></item><item><title><![CDATA[Conducting My First Cloud Symphony]]></title><description><![CDATA[I studied Music in high school. I learned about harmony, rhythm, and how individual instruments come together to create a single piece. I never thought those concepts would appear in my tech career, but as I began learning Google Kubernetes Engine (G...]]></description><link>https://blog.lxmwaniky.me/conducting-my-first-cloud-symphony</link><guid isPermaLink="true">https://blog.lxmwaniky.me/conducting-my-first-cloud-symphony</guid><category><![CDATA[Devops]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[kubectl]]></category><category><![CDATA[gke]]></category><category><![CDATA[google cloud]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Mon, 18 Aug 2025 01:18:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755476111617/69f63b35-2765-4961-b79b-abe83b1f0b0c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I studied Music in high school. I learned about harmony, rhythm, and how individual instruments come together to create a single piece. I never thought those concepts would appear in my tech career, but as I began learning <a target="_blank" href="https://cloud.google.com/kubernetes-engine/docs/quickstarts/create-cluster">Google Kubernetes Engine</a> (GKE), I realised they were surprisingly relevant.</p>
<p>This post is a detailed log of my first time deploying a real application on GKE. I'm starting from scratch, explaining the core concepts, the commands I used, and, most importantly, the errors I hit and how I fixed them. If you're new to this, I hope my journey helps clarify yours a bit.</p>
<h3 id="heading-before-we-start-the-core-concepts">Before We Start: The Core Concepts</h3>
<p>Before we even touch the cloud, we need to get a few key terms straight. I had to wrap my head around these before anything else made sense.</p>
<h3 id="heading-1-what-is-a-container-and-docker"><strong>1. What is a Container? (And Docker?)</strong></h3>
<p>A <strong>container</strong> is a standard package that holds everything your application needs to run: the code, its libraries, and its settings. The best analogy is a musician's custom-built flight case. It has the instrument, the sheet music, the stand—everything. With this case, the musician can show up to any venue in the world, open it up, and perform perfectly without needing anything from the venue itself.</p>
<p><strong>Docker</strong> is the tool you use to build these "flight cases." It’s a platform that lets you package your application into a portable container image.</p>
<h3 id="heading-2-what-is-kubernetes-often-called-k8s"><strong>2. What is Kubernetes? (often called K8s)</strong></h3>
<p>If one container is one musician, what happens when your application gets big and you need a whole orchestra of them? This is where Kubernetes comes in. <strong>Kubernetes</strong> is the <strong>conductor</strong>. It's an open-source system that manages all of your containers (the orchestra) for you. Its job is to:</p>
<ul>
<li><p><strong>Schedule:</strong> Decide which musician plays on which part of the stage.</p>
</li>
<li><p><strong>Scale:</strong> If a song needs more violins, it brings on more violinists (adds more containers).</p>
</li>
<li><p><strong>Self-heal:</strong> If a trumpeter suddenly gets sick (a container crashes), it immediately brings in a replacement so the show goes on.</p>
</li>
</ul>
<p><strong>3. What is Google Kubernetes Engine (GKE)?</strong><br />While Kubernetes is the conductor, it still needs a place to perform. <strong>GKE</strong> is a world-class <strong>concert hall</strong> managed by Google. You could build your concert hall (run Kubernetes on your servers), but that's a massive amount of work. GKE provides a ready-to-go, reliable environment. Google's team handles the difficult parts—like the building's electricity, security, and maintenance—so you can focus purely on being the conductor and making sure your application (the music) is great.</p>
<p>With that, let's get started.</p>
<h3 id="heading-step-1-setting-up-the-concert-hall-the-gke-cluster">Step 1: Setting Up the Concert Hall (The GKE Cluster)</h3>
<p>The first step was to create a <strong>cluster</strong>. A cluster is the foundation of GKE—it's a set of computer resources (called "nodes") that will run our containers. This is the digital venue where our performance will take place.</p>
<p>I chose to create my cluster in <strong>Autopilot</strong> mode. In GKE, you have two choices:</p>
<ul>
<li><p><strong>Standard Mode:</strong> You manually choose the size and number of machines (nodes) for your cluster. You have more control, but you're also responsible for managing them.</p>
</li>
<li><p><strong>Autopilot Mode:</strong> You just tell GKE to run your applications, and it figures out how much computing power is needed. It automatically adds or removes nodes as required.</p>
</li>
</ul>
<p>For a beginner, Autopilot is perfect. You don't have to worry about managing the underlying servers, and you only pay for the resources your apps consume. After a few minutes of setup in the <a target="_blank" href="https://console.cloud.google.com/kubernetes/">Google Cloud Console</a>, my cluster was live.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478027084/cafb43ba-1271-4724-aa1f-2f2db1e1074f.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-2-getting-the-first-musician-on-stage-deploying-an-app">Step 2: Getting the First Musician on Stage (Deploying an App)</h3>
<p>Now that the venue was ready, it was time to deploy my first application. All interactions with a Kubernetes cluster happen through a command-line tool called <a target="_blank" href="http://kubernetes.io/docs/reference/kubectl/">kubectl</a>. Think of it as the conductor's baton; it’s the tool we use to give instructions to our orchestra.</p>
<p>I started with a command to create a <strong>Deployment</strong>.</p>
<pre><code class="lang-bash">kubectl create deployment hello-server --image=us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
</code></pre>
<p>A Deployment is a Kubernetes object that acts as a manager for your app. You tell it, "I want one copy of my 'hello-server' app running at all times." The Deployment's job is to make that happen. If your app crashes, the Deployment will automatically start a new one. It’s like hiring a dedicated manager for your string section, whose only job is to ensure the right number of musicians are always on stage and ready to play.</p>
<p>I hit enter and immediately got an error.</p>
<pre><code class="lang-bash">error: failed to create deployment... connect: connection refused
</code></pre>
<p>This was my first real-world debugging moment. The error meant my kubectl tool wasn't authenticated to talk to my new GKE cluster. The fix was to run a gcloud command to fetch the credentials and configure kubectl for me. This command essentially connected my conductor's podium to the concert hall's sound system, so my instructions could finally be heard.</p>
<pre><code class="lang-bash">gcloud container clusters get-credentials musical-cluster --region us-central1
</code></pre>
<p>I ran the deployment command again, and this time it worked. My app was running.</p>
<p>However, it was only running <em>inside</em> the cluster. No one from the outside world could access it. To make it publicly available, I had to create a <strong>Service</strong>.</p>
<pre><code class="lang-bash">kubectl expose deployment hello-server --<span class="hljs-built_in">type</span>=LoadBalancer --port 80 --target-port 8080
</code></pre>
<p>A Service provides a stable network endpoint (like an IP address) for your application. By setting type=LoadBalancer, I told GKE to provision a real, external cloud load balancer from Google's network, assign it a public IP, and automatically send any traffic to my running app. In our analogy, this is like setting up the main entrance and hiring ushers. The ushers (the Load Balancer) guide the audience to the musician who is ready to play.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478373273/2535ed0d-2326-4770-8003-bb185bb3e8a5.png" alt class="image--center mx-auto" /></p>
<p>After a minute, the external IP was ready. I pasted it into my browser, and there it was.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478399425/33e8520d-a0ad-42f4-8f1d-16fbb2d51067.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-3-adding-more-musicians-scaling-the-application">Step 3: Adding More Musicians (Scaling the Application)</h3>
<p>My single app was running fine, but a real application needs to handle lots of users. This means I needed to scale out by adding more copies, or <strong>replicas</strong>.</p>
<p>This is where the power of the Deployment object shows. With a single wave of the conductor's baton, I told the orchestra manager to expand the section.</p>
<pre><code class="lang-bash">kubectl scale deployment hello-server --replicas=3
</code></pre>
<p>This command updated the Deployment object, changing its desired state from 1 replica to 3. Kubernetes immediately saw the difference and started two new, identical copies of my app to match the new state.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478515731/73622172-a5ac-4ab6-bd63-ea41cec18964.png" alt class="image--center mx-auto" /></p>
<p>The Service I created in the previous step automatically detected these new copies and started distributing traffic between all three of them. I could prove this by going back to the browser and refreshing the page. The Hostname value at the bottom changed every few refreshes, showing my request was being handled by a different replica each time. It was a simple but powerful demonstration of scaling and load balancing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478537955/3ede5b5c-f65c-40af-ac13-08aaeb1ef2df.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478574306/30d608b3-8483-4bb6-a0b1-e4191c2c6c48.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-4-the-update-that-almost-failed">Step 4: The Update That Almost Failed</h3>
<p>The final test was to update the application to a new version, 2.0.0, without any downtime. Kubernetes is designed to do this with a <strong>rolling update</strong> strategy. It replaces old app containers with new ones gradually, one by one, ensuring the application is always available. The goal was to give all the musicians a new sheet of music without interrupting the concert for the audience.</p>
<p>I ran the command to tell my Deployment to use the new image:</p>
<pre><code class="lang-bash">kubectl <span class="hljs-built_in">set</span> image deployment/hello-server hello-app=us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0
</code></pre>
<p>I watched the status and saw that GKE was trying to start a new container with the 2.0 image, but it got stuck in a Pending state. This meant the new musician, holding the new sheet music, was waiting backstage, but there was no empty chair for them on the stage. The good news was that my old 1.0 replicas were still running, so my website was still live. But the update was failing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478708268/96d0a6d0-6bd8-4815-9ab8-908b9b869a1c.png" alt class="image--center mx-auto" /></p>
<p>To find out why, I used the most important command for Kubernetes troubleshooting: kubectl describe pod. This gives you a detailed event log for a specific pod.</p>
<pre><code class="lang-bash">kubectl describe pod &lt;name-of-the-pending-pod&gt;
</code></pre>
<p>The answer was in the Events section at the bottom. The analogy clicked instantly. My stage crew (Autopilot) went to the lumberyard (Google's infrastructure) to get wood for a new chair, but the yard owner said, 'Sorry, your account has hit its safety limit for today. No more materials for you.'</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478742197/4871322c-8b34-4faa-a2f5-417f366918fe.png" alt class="image--center mx-auto" /></p>
<p>This was a huge learning moment. All Google Cloud projects have <strong>quotas</strong>, which are safety limits on resource usage. A rolling update temporarily requires <em>more</em> resources than normal because, for a moment, both old and new app replicas need to exist. My project's quota was too low to allow for this temporary increase.</p>
<p>Here’s the workaround I followed:</p>
<ol>
<li><p>First, I undid the failed update to get back to a stable state:</p>
<pre><code class="lang-bash"> kubectl rollout undo deployment/hello-server
</code></pre>
</li>
<li><p>Next, I made some room by scaling the old application down from 3 replicas to 2:</p>
<pre><code class="lang-bash"> kubectl scale deployment hello-server --replicas=2
</code></pre>
</li>
<li><p>With the resources freed up, I ran the set image update command again. This time it worked, replacing the two old replicas with two new ones.</p>
</li>
<li><p>Finally, I scaled the now-updated application back up to 3 replicas:</p>
<pre><code class="lang-bash"> kubectl scale deployment hello-server --replicas=3
</code></pre>
</li>
</ol>
<p>It was a valuable lesson in how cloud resource limits affect application management. After that, I refreshed my browser and saw "Version: 2.0.0." The update was complete.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755478872064/fc0ebe96-8fde-41a5-9ea2-77190ca93918.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>My first dive into GKE was a success. The most important takeaway for me was that the errors were the best part of the experience. Hitting the authentication and quota issues forced me to learn how to debug, which is a far more useful skill than just following a tutorial that works perfectly. I was also genuinely impressed with how GKE automates complex tasks like scaling and load balancing with just a few commands.</p>
<p>The concert is just getting started.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding APIPA]]></title><description><![CDATA[What Is APIPA and Why Should You Care?
Have you ever connected your computer to a network and found that you can't access the internet, but you can somehow still see other computers nearby? You might have encountered APIPA without knowing it.
APIPA s...]]></description><link>https://blog.lxmwaniky.me/understanding-apipa</link><guid isPermaLink="true">https://blog.lxmwaniky.me/understanding-apipa</guid><category><![CDATA[apipa]]></category><category><![CDATA[Network troubleshooting]]></category><category><![CDATA[networking]]></category><category><![CDATA[Network Engineering]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sat, 16 Aug 2025 10:19:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755338449036/d2279629-3097-430a-8c74-e232ef591a85.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-apipa-and-why-should-you-care">What Is APIPA and Why Should You Care?</h2>
<p>Have you ever connected your computer to a network and found that you can't access the internet, but you can somehow still see other computers nearby? You might have encountered APIPA without knowing it.</p>
<p>APIPA stands for "Automatic Private IP Addressing." Think of it as your computer's emergency backup plan when things go wrong with your network connection.</p>
<h2 id="heading-how-networks-usually-work">How Networks Usually Work</h2>
<p>Normally, when you connect your computer to a network, it asks a special server called a DHCP server: "Hey, can I get an IP address so I can join this network?" The DHCP server then gives your computer all the information it needs:</p>
<ul>
<li><p>An IP address (like 192.168.1.100)</p>
</li>
<li><p>A gateway (the door to the internet)</p>
</li>
<li><p>DNS servers (phone books for the internet)</p>
</li>
</ul>
<p>This is like checking into a hotel - the front desk gives you a room number and tells you how to find the elevator and restaurant.</p>
<h2 id="heading-when-things-go-wrong">When Things Go Wrong</h2>
<p>But what happens when the DHCP server is broken, unreachable, or simply doesn't exist? Your computer doesn't just give up. Instead, it says, "Fine, I'll make my IP address!"</p>
<p>This is where APIPA kicks in.</p>
<h2 id="heading-what-apipa-does">What APIPA Does</h2>
<p>When your computer can't get an IP address from a DHCP server, APIPA automatically assigns itself an address that starts with <strong>169.254</strong>. You'll see something like:</p>
<ul>
<li><p>169.254.23.45</p>
</li>
<li><p>169.254.156.78</p>
</li>
<li><p>169.254.1.200</p>
</li>
</ul>
<p>Think of this as your computer creating a temporary name tag for itself when there's no official host to give it one.</p>
<h2 id="heading-the-good-and-bad-of-apipa">The Good and Bad of APIPA</h2>
<p><strong>The Good:</strong></p>
<ul>
<li><p>Computers with APIPA addresses can talk to each other on the same network</p>
</li>
<li><p>You can share files between nearby computers</p>
</li>
<li><p>It's better than having no network connection at all</p>
</li>
</ul>
<p><strong>The Bad:</strong></p>
<ul>
<li><p>No internet access (no gateway to the outside world)</p>
</li>
<li><p>Can't reach devices outside your immediate network</p>
</li>
<li><p>Usually means something is broken</p>
</li>
</ul>
<h2 id="heading-how-to-spot-apipa">How to Spot APIPA</h2>
<p>On Windows, open Command Prompt and type:</p>
<pre><code class="lang-bash">ipconfig
</code></pre>
<p>If you see an IP address starting with 169.254, you've got APIPA.</p>
<p><img src="https://vivotek.zendesk.com/hc/article_attachments/900008308026/rtaImage_-_2021-05-09T221829.053.jfif" alt class="image--center mx-auto" /></p>
<p>On the surface, your computer might look "connected" to the network, but you'll notice:</p>
<ul>
<li><p>Websites won't load</p>
</li>
<li><p>Email won't work</p>
</li>
<li><p>You can't reach anything outside your local area</p>
</li>
</ul>
<h2 id="heading-what-apipa-means">What APIPA Means</h2>
<p>When you see a 169.254 address, your computer is essentially saying: "I tried to join the network properly, but something went wrong. I'm doing the best I can with what I have."</p>
<p>It's like showing up to a conference where the registration desk is closed - you can still talk to other people who showed up, but you can't get into the main event.</p>
<h2 id="heading-real-world-examples-of-apipa">Real-World Examples of APIPA</h2>
<h3 id="heading-example-1-the-home-router-power-outage">Example 1: The Home Router Power Outage</h3>
<p>Your home internet was working fine yesterday. Today, your computer shows it's connected to your WiFi, but you can't browse anything. After a power outage last night, your router came back online, but the DHCP service inside it didn't start properly. Your computer connects to the WiFi signal but gets an APIPA address like 169.254.78.234 instead of your usual 192.168.1.something.</p>
<h3 id="heading-example-2-the-new-ethernet-cable">Example 2: The New Ethernet Cable</h3>
<p>You just moved your desk and bought a new 50-foot Ethernet cable from the store. Everything plugs in fine, but you keep getting 169.254 addresses. Turns out, the cheap cable you bought has a broken wire inside - your computer can partially communicate with the network but can't reach the DHCP server reliably.</p>
<h3 id="heading-example-3-the-overpacked-office-network">Example 3: The Overpacked Office Network</h3>
<p>It's Monday morning in a busy office. The DHCP server is configured to hand out only 50 IP addresses, but 60 people are trying to connect their laptops after the weekend. The first 50 people get normal addresses like 192.168.1.25, but employees 51-60 end up with APIPA addresses and can't access company resources.</p>
<h3 id="heading-example-4-the-wrong-network-jack">Example 4: The Wrong Network Jack</h3>
<p>You plug your laptop into a wall jack in a conference room. The port seems to work (link lights are on), but you get 169.254.156.89. Later, you discover this wall jack connects to an isolated network segment used for security cameras, not the main office network with internet access.</p>
<h2 id="heading-common-scenarios-where-apipa-appears">Common Scenarios Where APIPA Appears</h2>
<ol>
<li><p><strong>Cable Problems</strong>: The Network cable is unplugged, damaged, or of poor quality</p>
</li>
<li><p><strong>Switch Issues</strong>: The network switch is down, misconfigured, or the port is disabled</p>
</li>
<li><p><strong>DHCP Server Problems</strong>: The server that hands out IP addresses is broken or overwhelmed</p>
</li>
<li><p><strong>Wrong Network</strong>: You're connected to a network segment that doesn't have DHCP</p>
</li>
<li><p><strong>Network Congestion</strong>: Too many devices are trying to get addresses from a limited pool</p>
</li>
<li><p><strong>VLAN Misconfiguration</strong>: Your computer is on the wrong VLAN and can't reach the DHCP server</p>
</li>
<li><p><strong>Router/Modem Issues</strong>: Home router's DHCP service crashed after a power outage</p>
</li>
</ol>
<h2 id="heading-troubleshooting-apipa-step-by-step">Troubleshooting APIPA: Step-by-Step</h2>
<h3 id="heading-level-1-quick-fixes-try-these-first">Level 1: Quick Fixes (Try These First)</h3>
<p><strong>Scenario</strong>: You just noticed your computer has a 169.254 address and no internet.</p>
<ol>
<li><p><strong>The Unplug-Replug Test</strong></p>
<ul>
<li><p>Unplug your Ethernet cable, wait 10 seconds, plug it back in</p>
</li>
<li><p>For WiFi: Disconnect and reconnect to the network</p>
</li>
<li><p>Wait 30 seconds and check your IP address again</p>
</li>
</ul>
</li>
<li><p><strong>The Network Restart</strong></p>
<ul>
<li><p>Right-click your network icon in the system tray</p>
</li>
<li><p>Select "Disable" then "Enable" your network adapter</p>
</li>
<li><p>Or open Command Prompt as administrator and type:</p>
<pre><code class="lang-bash">  ipconfig /release
  ipconfig /renew
</code></pre>
</li>
</ul>
</li>
<li><p><strong>The Computer Restart</strong></p>
<ul>
<li><p>Sometimes Windows gets confused - a restart clears everything</p>
</li>
<li><p>This fixes about 30% of APIPA issues</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-level-2-physical-investigation">Level 2: Physical Investigation</h3>
<p><strong>Scenario</strong>: Quick fixes didn't work, time to check the hardware.</p>
<ol>
<li><p><strong>Cable Detective Work</strong></p>
<ul>
<li><p>Try a different Ethernet cable if you have one</p>
</li>
<li><p>Look for visible damage: bent connectors, crushed cable</p>
</li>
<li><p>Test the same cable on a different computer</p>
</li>
<li><p>Wiggle the cable at both ends - does the connection drop?</p>
</li>
</ul>
</li>
<li><p><strong>Port Testing</strong></p>
<ul>
<li><p>Try a different wall jack or switch port</p>
</li>
<li><p>Check if the port lights up when you plug in</p>
</li>
<li><p>Ask a colleague if their computer works in the same location</p>
</li>
</ul>
</li>
<li><p><strong>WiFi Signal Check</strong></p>
<ul>
<li><p>Move closer to the WiFi router</p>
</li>
<li><p>Try connecting a different device (phone, tablet)</p>
</li>
<li><p>Check if you can see other WiFi networks from the same location</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-level-3-network-infrastructure">Level 3: Network Infrastructure</h3>
<p><strong>Scenario</strong>: Your connection is physically fine, but APIPA persists.</p>
<ol>
<li><p><strong>DHCP Server Health Check</strong></p>
<ul>
<li><p>Ask IT or check if others are having the same problem</p>
</li>
<li><p>For home networks: restart your router (unplug for 30 seconds)</p>
</li>
<li><p>Check the router admin panel for DHCP settings</p>
</li>
</ul>
</li>
<li><p><strong>Address Pool Investigation</strong></p>
<ul>
<li><p>In busy environments, DHCP might be out of addresses</p>
</li>
<li><p>Try connecting at different times of day</p>
</li>
<li><p>Ask the administrator to check the DHCP lease table</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-level-4-advanced-troubleshooting">Level 4: Advanced Troubleshooting</h3>
<p><strong>Scenario</strong>: Everything seems fine, but APIPA won't go away.</p>
<ol>
<li><p><strong>VLAN and Network Segmentation</strong></p>
<ul>
<li><p>You might be on the wrong network segment</p>
</li>
<li><p>Check with the network administrator about the proper VLAN assignment</p>
</li>
<li><p>Some wall jacks connect to isolated networks</p>
</li>
</ul>
</li>
<li><p><strong>Network Adapter Issues</strong></p>
<ul>
<li><p>Update network card drivers</p>
</li>
<li><p>Check Device Manager for yellow warning signs</p>
</li>
<li><p>Try a different network adapter if available</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-why-understanding-apipa-helps">Why Understanding APIPA Helps</h2>
<p>Recognising APIPA saves you time and frustration. Instead of wondering "Why is my internet so slow?" or "Why can't I reach anything?", you'll immediately know to look at the network infrastructure rather than your computer settings.</p>
<h2 id="heading-key-takeaway">Key Takeaway</h2>
<p>APIPA isn't the problem - it's a symptom that tells a story. When you see those 169.254 addresses, your computer is essentially saying, "I tried my best to join the network, but something in the infrastructure isn't working properly."</p>
<p>Think of APIPA as your network's equivalent of a car's "check engine" light. The light itself isn't broken - it's warning you that something else needs attention.</p>
<p><strong>Remember</strong>:</p>
<ul>
<li><p>APIPA = 169.254.x.x addresses</p>
</li>
<li><p>It means DHCP failed, not that your computer is broken</p>
</li>
<li><p>You can still communicate locally, but no internet access</p>
</li>
<li><p>It's always fixable once you find the root cause</p>
</li>
</ul>
<hr />
<p><em>Remember: Every network problem is solvable. The key is understanding what your computer is trying to tell you.</em></p>
<iframe src="/isolated-segment.html" style="display:none" sandbox="allow-scripts allow-same-origin"></iframe>]]></content:encoded></item><item><title><![CDATA[My Experience with NGINX]]></title><description><![CDATA[Recently, I started working with servers and came across NGINX. Honestly, I had no idea what to expect. All I wanted was to serve a simple website on my VPS and keep the domain name active. I thought it would be as easy as uploading files and calling...]]></description><link>https://blog.lxmwaniky.me/my-experience-with-nginx</link><guid isPermaLink="true">https://blog.lxmwaniky.me/my-experience-with-nginx</guid><category><![CDATA[nginx]]></category><category><![CDATA[server]]></category><category><![CDATA[Load Balancing]]></category><category><![CDATA[Reverse Proxy]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 10 Aug 2025 03:28:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754796450684/da60ce33-9b3c-49b3-9a7e-2c8aeec395c2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, I started working with servers and came across NGINX. Honestly, I had no idea what to expect. All I wanted was to serve a simple website on my VPS and keep the domain name active. I thought it would be as easy as uploading files and calling it a day. However, it turns out there’s a lot more to it.  </p>
<p>When I set up NGINX, the first thing I encountered was its default page. It was clean and simple, but not what I needed. My goal was to display my website instead, so I began exploring how everything works. That’s when I realised that NGINX is built around configuration files. It has a default setup that works well for testing, but if you want to serve your site, you need to configure it yourself.  </p>
<p>I created a new config file for my site and linked it to NGINX, expecting everything to work immediately. However, my website was still missing, and I kept landing on the default page. After some digging, I learned that the default config was still active, so I had to disable it and properly enable mine. It seems simple now, but at the time, I struggled to understand what details I was missing.  </p>
<p>While working through this, I stumbled upon the concept of a reverse proxy. At first, it felt complicated, but after reading and experimenting, I realised how useful it is. A reverse proxy sits in the middle, taking user requests and forwarding them to another server. This not only protects your backend but also helps manage traffic more effectively.  </p>
<p>I also explored the idea of load balancing. Although I’m not using multiple servers yet, I found it fascinating how NGINX can distribute traffic across several servers to prevent any single server from becoming overwhelmed. For now, it’s just the knowledge I’m storing for when I eventually scale my projects.  </p>
<p>I’m eager to dive deeper into NGINX, especially as my projects grow and I take on more challenging setups.  </p>
<p>If you’ve worked with NGINX, I’d love to hear about your first experience. Let’s exchange ideas and keep learning!</p>
]]></content:encoded></item><item><title><![CDATA[Scaling to Enterprise: Multi-Department VLANs with CPU-Optimized Security]]></title><description><![CDATA[Module 7 of my MikroTik Zero to Hero Challenge
Time to go big! Module 7 was about scaling from a simple guest network to a full enterprise architecture. My mission: design a complete four-department network for Tech Innovators Kenya Ltd with proper s...]]></description><link>https://blog.lxmwaniky.me/mikrotik-enterprise-vlans-scalable-security</link><guid isPermaLink="true">https://blog.lxmwaniky.me/mikrotik-enterprise-vlans-scalable-security</guid><category><![CDATA[Address Lists]]></category><category><![CDATA[#Mikrotik]]></category><category><![CDATA[VLAN]]></category><category><![CDATA[Enterprise Networking]]></category><category><![CDATA[Security]]></category><category><![CDATA[firewall]]></category><category><![CDATA[RouterOS]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 10 Aug 2025 03:18:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754795679629/f800ab19-a1a9-49dc-b333-4530b64d8352.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Module 7 of my MikroTik Zero to Hero Challenge</em></p>
<p>Time to go big! Module 7 was about scaling from a simple guest network to a full enterprise architecture. My mission: design a complete four-department network for Tech Innovators Kenya Ltd with proper security policies that won't kill the router's CPU.</p>
<h2 id="heading-the-enterprise-challenge">The Enterprise Challenge</h2>
<p><strong>The Goal</strong>: Scale from simple guest WiFi to complete enterprise network segmentation<br /><strong>The Departments</strong>: Management, Development, Sales, plus Guests (4 VLANs total)<br /><strong>The Challenge</strong>: Create security policies that are both effective and CPU-efficient<br /><strong>The Reality</strong>: Small router hardware handling enterprise-level complexity</p>
<h2 id="heading-building-the-complete-vlan-infrastructure">Building the Complete VLAN Infrastructure</h2>
<p>I took initiative and built out the Development and Sales VLANs independently, applying the patterns I'd learned from the Guest network.</p>
<h3 id="heading-the-complete-network-design">The Complete Network Design</h3>
<p><strong>Management VLAN 10</strong>: 192.168.10.1/24 (executives, high security)<br /><strong>Development VLAN 20</strong>: 192.168.20.1/24 (engineers, development resources)<br /><strong>Sales VLAN 30</strong>: 192.168.30.1/24 (marketing, sales operations)<br /><strong>Guest VLAN 40</strong>: 192.168.40.1/24 (visitors, internet-only access)<br /><strong>Main LAN</strong>: 192.168.88.1/24 (original infrastructure)</p>
<p>Each department gets:</p>
<ul>
<li><p>Its IP address range</p>
</li>
<li><p>Dedicated DHCP server</p>
</li>
<li><p>Automatic DNS configuration</p>
</li>
<li><p>Gateway services through the router</p>
</li>
</ul>
<h2 id="heading-the-thats-too-basic-moment">The "That's Too Basic" Moment</h2>
<p>After setting up all the VLANs, I had a realisation: <em>"Management department being so secure... it seems so basic"</em></p>
<p><strong>The Problem</strong>: Just separating networks into VLANs doesn't provide security - it's just organization.</p>
<p><strong>The Reality Check</strong>: Network separation without security policies is like having separate rooms in a house with no locks on the doors.</p>
<p>This led to understanding the difference between <strong>network organization</strong> and <strong>security implementation</strong>.</p>
<h2 id="heading-the-scalability-question">The Scalability Question</h2>
<p>Looking at my growing list of VLANs, I asked the critical question: <em>"If I have 20 departments, won't managing individual firewall rules become impossible?"</em></p>
<p>This is where enterprise networking gets real. You need scalable security architectures, not individual rules for every department.</p>
<h2 id="heading-three-approaches-to-enterprise-security">Three Approaches to Enterprise Security</h2>
<h3 id="heading-method-1-individual-rules-doesnt-scale">Method 1: Individual Rules (Doesn't Scale)</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># This approach becomes unmanageable quickly</span>
/ip firewall filter add chain=forward src-address=192.168.10.0/24 dst-address=192.168.20.0/24 action=drop
/ip firewall filter add chain=forward src-address=192.168.10.0/24 dst-address=192.168.30.0/24 action=drop
<span class="hljs-comment"># ... (multiply this by every department pair)</span>
</code></pre>
<h3 id="heading-method-2-address-lists-medium-enterprise">Method 2: Address Lists (Medium Enterprise)</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Group networks into logical security zones</span>
/ip firewall address-list add list=Internal-Networks address=192.168.10.0/24
/ip firewall address-list add list=Internal-Networks address=192.168.20.0/24
/ip firewall address-list add list=Internal-Networks address=192.168.30.0/24
</code></pre>
<h3 id="heading-method-3-vlan-based-firewall-true-enterprise">Method 3: VLAN-Based Firewall (True Enterprise)</h3>
<p>This is what major ISPs and enterprises use - firewall rules that operate at the VLAN level rather than the IP level.</p>
<p><strong>My Choice</strong>: Method 2 for practical learning, with recognition that Method 3 is the enterprise standard.</p>
<h2 id="heading-building-production-grade-security-architecture">Building Production-Grade Security Architecture</h2>
<h3 id="heading-creating-security-zones">Creating Security Zones</h3>
<p>Instead of thinking about individual departments, I organised networks into security zones:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Department-specific lists</span>
/ip firewall address-list add list=Management address=192.168.10.0/24 comment=<span class="hljs-string">"Management Department"</span>
/ip firewall address-list add list=Development address=192.168.20.0/24 comment=<span class="hljs-string">"Development Department"</span>  
/ip firewall address-list add list=Sales address=192.168.30.0/24 comment=<span class="hljs-string">"Sales Department"</span>
/ip firewall address-list add list=Guests address=192.168.40.0/24 comment=<span class="hljs-string">"Guest Network"</span>

<span class="hljs-comment"># Security zone groupings</span>
/ip firewall address-list add list=Internal-Networks address=192.168.10.0/24 comment=<span class="hljs-string">"Management"</span>
/ip firewall address-list add list=Internal-Networks address=192.168.20.0/24 comment=<span class="hljs-string">"Development"</span>  
/ip firewall address-list add list=Internal-Networks address=192.168.30.0/24 comment=<span class="hljs-string">"Sales"</span>
/ip firewall address-list add list=Privileged-Networks address=192.168.10.0/24 comment=<span class="hljs-string">"Management - Full Access"</span>
/ip firewall address-list add list=Restricted-Networks address=192.168.40.0/24 comment=<span class="hljs-string">"Guests - Internet Only"</span>
</code></pre>
<h3 id="heading-the-four-rule-security-policy">The Four-Rule Security Policy</h3>
<p>Instead of potentially 20+ individual rules, I created just 4 strategic rules:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Rule 1: Management gets access to everything (executive oversight)</span>
/ip firewall filter add chain=forward src-address-list=Privileged-Networks action=accept comment=<span class="hljs-string">"Management full access"</span>

<span class="hljs-comment"># Rule 2: Guests can't reach company resources (security boundary)  </span>
/ip firewall filter add chain=forward src-address-list=Restricted-Networks dst-address-list=Internal-Networks action=drop comment=<span class="hljs-string">"Guest isolation"</span>

<span class="hljs-comment"># Rule 3: All internal departments get internet access (business operations)</span>
/ip firewall filter add chain=forward src-address-list=Internal-Networks action=accept comment=<span class="hljs-string">"Internal internet access"</span>

<span class="hljs-comment"># Rule 4: Departments can't talk to each other (security segmentation)</span>
/ip firewall filter add chain=forward src-address-list=Internal-Networks dst-address-list=Internal-Networks action=drop comment=<span class="hljs-string">"Department isolation"</span>
</code></pre>
<p>This elegant solution:</p>
<ul>
<li><p>✅ Gives Management full network access</p>
</li>
<li><p>✅ Blocks guests from company resources</p>
</li>
<li><p>✅ Allows all departments internet access</p>
</li>
<li><p>✅ Prevents inter-department communication</p>
</li>
<li><p>✅ Scales to unlimited departments without adding rules</p>
</li>
</ul>
<h2 id="heading-router-on-a-stick-magic">Router-on-a-Stick Magic</h2>
<p><strong>The Concept</strong>: One router providing routing services for multiple VLANs.</p>
<p><strong>How It Works</strong>: The router automatically provides layer 3 routing between VLANs without additional configuration.</p>
<p><strong>The Result</strong>: Each department can reach its gateway (192.168.x.1) and the internet, but department-to-department traffic is controlled by firewall rules.</p>
<p>RouterOS handles this automatically - no complex routing protocols needed for basic inter-VLAN routing.</p>
<h2 id="heading-the-performance-reality-check">The Performance Reality Check</h2>
<h3 id="heading-hardware-limitations-discovered">Hardware Limitations Discovered</h3>
<p>My HAP Lite router started showing strain:</p>
<ul>
<li><p><strong>High CPU usage</strong> during complex operations</p>
</li>
<li><p><strong>Multiple DHCP servers</strong> (5 total) are consuming resources</p>
</li>
<li><p><strong>Complex firewall processing</strong> with address list evaluations</p>
</li>
<li><p><strong>Bridge learning overhead,</strong> managing MAC addresses across VLANs</p>
</li>
</ul>
<h3 id="heading-isp-professional-advice">ISP Professional Advice</h3>
<p>A friend working at an ISP warned me, <em>"MikroTik routers can struggle with high rule counts. Keep it efficient."</em></p>
<p><strong>The Trade-off</strong>: Functionality vs. performance on limited hardware.</p>
<p><strong>My Decision</strong>: Continue with the current setup for learning, but understand the scaling limitations.</p>
<h2 id="heading-enterprise-routing-in-action">Enterprise Routing in Action</h2>
<p>The final routing table showed the router handling multiple network segments:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># /ip route print</span>
0 A S  0.0.0.0/0           192.168.100.1    (default internet route)
1 ADC  192.168.10.0/24     Management-VLAN  (Management gateway)
2 ADC  192.168.20.0/24     Development-VLAN (Development gateway)  
3 ADC  192.168.30.0/24     Sales-VLAN       (Sales gateway)
4 ADC  192.168.40.0/24     Guest-VLAN       (Guest gateway)
5 ADC  192.168.88.0/24     LAN-Bridge       (Original LAN)
</code></pre>
<p>The router was simultaneously acting as:</p>
<ul>
<li><p>Gateway for 5 different networks</p>
</li>
<li><p>DHCP server for 5 different networks</p>
</li>
<li><p>Firewall enforcing inter-network policies</p>
</li>
<li><p>NAT provider for internet access</p>
</li>
</ul>
<h2 id="heading-key-technical-skills-demonstrated">Key Technical Skills Demonstrated</h2>
<h3 id="heading-scalable-architecture-design">Scalable Architecture Design</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Address list management (scales to unlimited departments)</span>
/ip firewall address-list add list=Security-Zone address=network-range comment=<span class="hljs-string">"description"</span>

<span class="hljs-comment"># Strategic firewall rules (minimal count, maximum coverage)</span>
/ip firewall filter add chain=forward src-address-list=<span class="hljs-built_in">source</span> dst-address-list=destination action=policy comment=<span class="hljs-string">"purpose"</span>

<span class="hljs-comment"># VLAN interface creation pattern</span>
/interface vlan add name=<span class="hljs-string">"Department-VLAN"</span> vlan-id=XX interface=LAN-Bridge
/ip address add address=192.168.XX.1/24 interface=Department-VLAN
</code></pre>
<h3 id="heading-performance-conscious-implementation">Performance-Conscious Implementation</h3>
<ul>
<li><p><strong>Rule efficiency</strong>: Address lists instead of individual IP rules</p>
</li>
<li><p><strong>Processing optimisation</strong>: Strategic rule placement for minimal CPU impact</p>
</li>
<li><p><strong>Scalable design</strong>: Architecture supporting growth without linear complexity increase</p>
</li>
</ul>
<h2 id="heading-what-i-achieved">What I Achieved</h2>
<p><strong>Complete Enterprise Network Infrastructure</strong>:</p>
<ul>
<li><p>✅ <strong>4-department VLAN segmentation</strong> with proper IP addressing</p>
</li>
<li><p>✅ <strong>Scalable security framework</strong> supporting unlimited department growth</p>
</li>
<li><p>✅ <strong>CPU-optimised firewall rules</strong> (4 rules instead of 20+)</p>
</li>
<li><p>✅ <strong>Professional inter-VLAN routing</strong> with centralised internet access</p>
</li>
<li><p>✅ <strong>Department-specific DHCP services</strong> with automatic configuration</p>
</li>
</ul>
<p><strong>Security Policy Implementation</strong>:</p>
<ul>
<li><p>✅ <strong>Management privilege elevation</strong> (executive access to all resources)</p>
</li>
<li><p>✅ <strong>Guest network isolation</strong> (internet-only access)</p>
</li>
<li><p>✅ <strong>Department segmentation</strong> (preventing lateral movement)</p>
</li>
<li><p>✅ <strong>Centralised internet access</strong> (controlled outbound connectivity)</p>
</li>
</ul>
<h2 id="heading-from-small-office-to-enterprise-scale">From Small Office to Enterprise Scale</h2>
<p>Module 7 was where I stopped thinking like a home user and started thinking like a network architect.</p>
<p>The key insights:</p>
<ol>
<li><p><strong>Network organization ≠ Network security</strong> - VLANs need security policies</p>
</li>
<li><p><strong>Scalability matters from day one</strong> - Architecture decisions affect long-term maintainability</p>
</li>
<li><p><strong>Performance is always a constraint</strong> - Enterprise features require enterprise hardware</p>
</li>
<li><p><strong>Address lists are powerful</strong> - They enable policy-based networking at scale</p>
</li>
</ol>
<h2 id="heading-the-real-world-connection">The Real-World Connection</h2>
<p>The security architecture I built mirrors what you'd find in:</p>
<ul>
<li><p><strong>Corporate offices</strong> (department-based network segmentation)</p>
</li>
<li><p><strong>Hotels</strong> (guest isolation with different service levels)</p>
</li>
<li><p><strong>Schools</strong> (student/faculty/admin network separation)</p>
</li>
<li><p><strong>Hospitals</strong> (patient/medical/administrative network isolation)</p>
</li>
</ul>
<p>The scale changes, but the fundamental concepts remain the same.</p>
<p>Understanding these patterns prepared me for the next phase of the challenge - where network complexity would increase even further with load balancing, VPNs, and advanced traffic management.</p>
<hr />
<p><em>This is part of my MikroTik Zero to Hero challenge. Enterprise networking: where architecture decisions made early determine how far you can scale.</em></p>
]]></content:encoded></item><item><title><![CDATA[Building Professional WiFi: Secure Employee Network + Isolated Guest Access]]></title><description><![CDATA[Module 6 of my MikroTik Zero to Hero Challenge
Time to tackle wireless networking! My mission: create a professional WiFi setup for "Tech Innovators Kenya Ltd" - a software development company needing secure employee WiFi plus isolated guest access. ...]]></description><link>https://blog.lxmwaniky.me/mikrotik-secure-wifi-isolated-guest-access</link><guid isPermaLink="true">https://blog.lxmwaniky.me/mikrotik-secure-wifi-isolated-guest-access</guid><category><![CDATA[Guest Network]]></category><category><![CDATA[#Mikrotik]]></category><category><![CDATA[wifi]]></category><category><![CDATA[VLAN]]></category><category><![CDATA[wireless network]]></category><category><![CDATA[Security]]></category><category><![CDATA[dhcp]]></category><category><![CDATA[RouterOS]]></category><category><![CDATA[enterprise network solutions]]></category><category><![CDATA[troubleshooting]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 10 Aug 2025 03:10:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754795259871/8cc55367-eac1-4aee-bf7f-94c692e56848.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Module 6 of my MikroTik Zero to Hero Challenge</em></p>
<p>Time to tackle wireless networking! My mission: create a professional WiFi setup for "Tech Innovators Kenya Ltd" - a software development company needing secure employee WiFi plus isolated guest access. This is where networking gets real.</p>
<h2 id="heading-the-business-problem">The Business Problem</h2>
<p><strong>The Company</strong>: Tech Innovators Kenya Ltd (25 employees + daily visitors)<br /><strong>The Challenge</strong>: Provide WiFi for employees while keeping guest traffic completely separated<br /><strong>The Risk</strong>: Guests accessing company servers, client data, or internal resources<br /><strong>The Solution</strong>: Dual-SSID setup with VLAN isolation</p>
<h2 id="heading-starting-fresh-and-why-i-had-to">Starting Fresh (And Why I Had To)</h2>
<p>I hit a roadblock right away. My router was controlled by something called "CAPsMAN" from previous configuration attempts, and I couldn't control the wireless interface.</p>
<p><strong>Decision</strong>: Complete router reset and rebuild everything from scratch.</p>
<p><strong>Challenge accepted!</strong> I rebuilt Modules 1-5 from memory in just 10 minutes. This proved I'd learned the fundamentals, not just copied commands.</p>
<p>Within minutes, I had:</p>
<ul>
<li><p>System identity and time sync ✓</p>
</li>
<li><p>WAN/LAN separation ✓</p>
</li>
<li><p>DHCP server and IP pools ✓</p>
</li>
<li><p>Firewall rules and NAT ✓</p>
</li>
<li><p>Internet connectivity is working ✓</p>
</li>
</ul>
<h2 id="heading-designing-the-network-architecture">Designing the Network Architecture</h2>
<p><strong>Main Network (Employees)</strong>:</p>
<ul>
<li><p>SSID: <code>TechInnovators-Main</code></p>
</li>
<li><p>Network: 192.168.88.0/24 (existing LAN)</p>
</li>
<li><p>Access: Full company resources + internet</p>
</li>
</ul>
<p><strong>Guest Network (Visitors)</strong>:</p>
<ul>
<li><p>SSID: <code>TechInnovators-Guest</code></p>
</li>
<li><p>Network: 192.168.40.0/24 (new VLAN 40)</p>
</li>
<li><p>Access: Internet only, no company resources</p>
</li>
</ul>
<h2 id="heading-building-the-guest-network-infrastructure">Building the Guest Network Infrastructure</h2>
<h3 id="heading-step-1-create-the-guest-vlan">Step 1: Create the Guest VLAN</h3>
<pre><code class="lang-bash">/interface vlan add name=<span class="hljs-string">"Guest-VLAN"</span> vlan-id=40 interface=LAN-Bridge
/ip address add address=192.168.40.1/24 interface=Guest-VLAN
</code></pre>
<p>This creates a completely separate network segment for guests.</p>
<h3 id="heading-step-2-set-up-guest-dhcp-service">Step 2: Set Up Guest DHCP Service</h3>
<pre><code class="lang-bash">/ip pool add name=Guest-Pool ranges=192.168.40.100-192.168.40.150
/ip dhcp-server add name=Guest-DHCP interface=Guest-VLAN address-pool=Guest-Pool
/ip dhcp-server network add address=192.168.40.0/24 gateway=192.168.40.1 dns-server=8.8.8.8,1.1.1.1
</code></pre>
<p><strong>Important discovery</strong>: The DHCP server was created disabled! Had to enable it:</p>
<pre><code class="lang-bash">/ip dhcp-server <span class="hljs-built_in">enable</span> Guest-DHCP
</code></pre>
<p>RouterOS always creates DHCP servers in a disabled state for safety.</p>
<h3 id="heading-step-3-create-guest-wifi-security">Step 3: Create Guest WiFi Security</h3>
<pre><code class="lang-bash">/interface wireless security-profiles add name=<span class="hljs-string">"TechInnovators-Guest-Security"</span> mode=dynamic-keys authentication-types=wpa2-psk unicast-ciphers=aes-ccm group-ciphers=aes-ccm wpa2-pre-shared-key=<span class="hljs-string">"Guest2024!"</span>
</code></pre>
<p>Professional but visitor-friendly password that's easy to share.</p>
<h3 id="heading-step-4-create-the-guest-virtual-interface">Step 4: Create the Guest Virtual Interface</h3>
<pre><code class="lang-bash">/interface wireless add name=<span class="hljs-string">"wlan-guest"</span> master-interface=wlan1 ssid=<span class="hljs-string">"TechInnovators-Guest"</span> security-profile=<span class="hljs-string">"TechInnovators-Guest-Security"</span> vlan-mode=use-tag vlan-id=40
</code></pre>
<p>This creates a virtual WiFi interface that tags all traffic with VLAN 40.</p>
<h3 id="heading-step-5-connect-guest-wifi-to-guest-vlan">Step 5: Connect Guest WiFi to Guest VLAN</h3>
<pre><code class="lang-bash">/interface bridge port add interface=wlan-guest bridge=LAN-Bridge pvid=40
</code></pre>
<p>The <code>pvid=40</code> ensures all traffic from this interface gets VLAN 40 tags.</p>
<h2 id="heading-securing-the-main-employee-network">Securing the Main Employee Network</h2>
<p><strong>Security Problem Discovered</strong>: The main WiFi was created with no encryption - completely open!</p>
<p><strong>Professional Response</strong>: Immediate security implementation.</p>
<pre><code class="lang-bash">/interface wireless security-profiles add name=<span class="hljs-string">"TechInnovators-Main-Security"</span> mode=dynamic-keys authentication-types=wpa2-psk unicast-ciphers=aes-ccm group-ciphers=aes-ccm wpa2-pre-shared-key=<span class="hljs-string">"TechStaff2024!"</span>

/interface wireless <span class="hljs-built_in">set</span> wlan1 mode=ap-bridge ssid=<span class="hljs-string">"TechInnovators-Main"</span> security-profile=<span class="hljs-string">"TechInnovators-Main-Security"</span> disabled=no
</code></pre>
<h2 id="heading-the-critical-service-disruption-and-recovery">The Critical Service Disruption (And Recovery)</h2>
<p><strong>Disaster</strong>: While configuring VLANs, I accidentally deleted the main LAN DHCP server!</p>
<p><strong>Symptom</strong>: My laptop suddenly got <code>169.254.x.x</code> address and lost network access.</p>
<p><strong>Emergency Recovery</strong>:</p>
<pre><code class="lang-bash">/ip dhcp-server add name=LAN-DHCP interface=LAN-Bridge address-pool=LAN-Pool
/ip dhcp-server <span class="hljs-built_in">enable</span> LAN-DHCP
</code></pre>
<p><strong>Lesson learned</strong>: Always verify core services remain intact after making changes.</p>
<h2 id="heading-solving-guest-internet-access">Solving Guest Internet Access</h2>
<p>Guests could connect to WiFi, but couldn't browse the internet. The problem was in two places:</p>
<h3 id="heading-missing-nat-rule">Missing NAT Rule</h3>
<pre><code class="lang-bash">/ip firewall nat add chain=srcnat src-address=192.168.40.0/24 out-interface=<span class="hljs-string">"ISP Router"</span> action=masquerade comment=<span class="hljs-string">"Guest internet access"</span>
</code></pre>
<h3 id="heading-missing-firewall-rule">Missing Firewall Rule</h3>
<pre><code class="lang-bash">/ip firewall filter add chain=forward src-address=192.168.40.0/24 action=accept comment=<span class="hljs-string">"Allow guest to internet"</span>
</code></pre>
<p><strong>Success!</strong> Guests could now browse the internet while being completely isolated from the company network.</p>
<h2 id="heading-adding-professional-security-features">Adding Professional Security Features</h2>
<h3 id="heading-mac-address-filtering-for-employee-network">MAC Address Filtering for Employee Network</h3>
<pre><code class="lang-bash">/interface wireless access-list add interface=wlan1 mac-address=F4:30:B9:13:C1:55 authentication=yes comment=<span class="hljs-string">"Alex Laptop"</span>
/interface wireless <span class="hljs-built_in">set</span> wlan1 default-authentication=no
</code></pre>
<p>This creates a whitelist - only authorized devices can connect to the main network.</p>
<h3 id="heading-channel-optimization">Channel Optimization</h3>
<p>I set the wireless channel to 6 (2437 MHz) after analyzing the local spectrum for minimal interference.</p>
<h2 id="heading-network-isolation-verification">Network Isolation Verification</h2>
<p><strong>Employee Network Test</strong>:</p>
<ul>
<li><p>✅ Can access company resources (192.168.88.x)</p>
</li>
<li><p>✅ Can access the internet</p>
</li>
<li><p>✅ Can ping and manage the router</p>
</li>
</ul>
<p><strong>Guest Network Test</strong>:</p>
<ul>
<li><p>✅ Can access the internet</p>
</li>
<li><p>❌ Cannot reach company resources (192.168.88.x)</p>
</li>
<li><p>❌ Cannot ping the router management interface</p>
</li>
</ul>
<p>Perfect isolation achieved!</p>
<h2 id="heading-the-complete-architecture">The Complete Architecture</h2>
<p><strong>Traffic Flow - Employees</strong>:</p>
<pre><code class="lang-bash">Employee Device → wlan1 → LAN-Bridge → Company Resources + Internet
</code></pre>
<p><strong>Traffic Flow - Guests</strong>:</p>
<pre><code class="lang-bash">Guest Device → wlan-guest → VLAN 40 → Internet Only
</code></pre>
<p><strong>Security Boundary</strong>: VLAN-based isolation prevents any guest access to the company infrastructure.</p>
<h2 id="heading-professional-skills-demonstrated">Professional Skills Demonstrated</h2>
<h3 id="heading-security-first-mindset">Security-First Mindset</h3>
<ul>
<li><p>Immediate identification of open network vulnerability</p>
</li>
<li><p>Implementation of WPA2 encryption and MAC filtering</p>
</li>
<li><p>Multiple layers of defence (VLAN isolation + firewall rules)</p>
</li>
</ul>
<h3 id="heading-troubleshooting-under-pressure">Troubleshooting Under Pressure</h3>
<ul>
<li><p>Rapid diagnosis of DHCP server deletion</p>
</li>
<li><p>Systematic approach to connectivity issues</p>
</li>
<li><p>Layer-by-layer verification (DHCP → NAT → Firewall)</p>
</li>
</ul>
<h3 id="heading-enterprise-architecture-understanding">Enterprise Architecture Understanding</h3>
<ul>
<li><p>Recognition that a single router setup has scaling limitations</p>
</li>
<li><p>Understanding of how dedicated APs work in real businesses</p>
</li>
<li><p>Foundation concepts applicable to enterprise equipment</p>
</li>
</ul>
<h2 id="heading-key-commands-for-wireless-networks">Key Commands for Wireless Networks</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># VLAN Creation</span>
/interface vlan add name=<span class="hljs-string">"Guest-VLAN"</span> vlan-id=40 interface=LAN-Bridge
/ip address add address=192.168.40.1/24 interface=Guest-VLAN

<span class="hljs-comment"># Wireless Security Profiles</span>
/interface wireless security-profiles add name=<span class="hljs-string">"Profile-Name"</span> mode=dynamic-keys authentication-types=wpa2-psk unicast-ciphers=aes-ccm group-ciphers=aes-ccm wpa2-pre-shared-key=<span class="hljs-string">"Password"</span>

<span class="hljs-comment"># Virtual Wireless Interface</span>
/interface wireless add name=<span class="hljs-string">"wlan-guest"</span> master-interface=wlan1 ssid=<span class="hljs-string">"Network-Name"</span> security-profile=<span class="hljs-string">"Security-Profile"</span> vlan-mode=use-tag vlan-id=40

<span class="hljs-comment"># Bridge Integration with VLAN</span>
/interface bridge port add interface=wlan-guest bridge=LAN-Bridge pvid=40

<span class="hljs-comment"># MAC Address Filtering</span>
/interface wireless access-list add interface=wlan1 mac-address=MAC-ADDRESS authentication=yes
/interface wireless <span class="hljs-built_in">set</span> wlan1 default-authentication=no
</code></pre>
<h2 id="heading-what-i-achieved">What I Achieved</h2>
<p><strong>Final Network Architecture</strong>:</p>
<ul>
<li><p><strong>Main SSID</strong>: TechInnovators-Main (WPA2, MAC filtering, full access)</p>
</li>
<li><p><strong>Guest SSID</strong>: TechInnovators-Guest (WPA2, internet only, isolated)</p>
</li>
<li><p><strong>Security</strong>: Complete separation between employee and visitor traffic</p>
</li>
<li><p><strong>Automation</strong>: Both networks provide automatic IP assignment</p>
</li>
<li><p><strong>Compliance</strong>: Network isolation supporting data protection requirements</p>
</li>
</ul>
<h2 id="heading-from-basic-connectivity-to-professional-infrastructure">From Basic Connectivity to Professional Infrastructure</h2>
<p>Module 6 was a huge leap forward. I went from understanding basic wireless concepts to implementing enterprise-grade network segmentation.</p>
<p>The accidental DHCP server deletion was valuable - it proved I could diagnose and recover from service disruptions under pressure. That's a critical skill for any network administrator.</p>
<p>Most importantly, I learned that <strong>professional networking isn't just about making things work - it's about making them work securely, reliably, and with proper isolation between different user groups</strong>.</p>
<p>The guest network isolation I built here is the same concept used in hotels, coffee shops, and corporate offices worldwide. The scale changes, but the fundamental principles remain the same.</p>
<hr />
<p><em>This is part of my MikroTik Zero to Hero challenge. Professional wireless networking: harder than it looks, more rewarding than expected.</em></p>
<p><strong>Next up</strong>: Module 7 - VLANs &amp; Advanced Switching (Scaling to full enterprise department segmentation)</p>
]]></content:encoded></item><item><title><![CDATA[Double NAT Reality Check: Why My Port Forwarding Dreams Were Crushed]]></title><description><![CDATA[Module 5 of my MikroTik Zero to Hero Challenge
I started Module 5 thinking I'd learn some simple NAT rules and set up port forwarding. What I discovered instead was that most home networks don't work the way networking tutorials assume. Welcome to th...]]></description><link>https://blog.lxmwaniky.me/double-nat-port-forwarding-mikrotik</link><guid isPermaLink="true">https://blog.lxmwaniky.me/double-nat-port-forwarding-mikrotik</guid><category><![CDATA[Double NAT]]></category><category><![CDATA[#Mikrotik]]></category><category><![CDATA[nat]]></category><category><![CDATA[port-forwarding]]></category><category><![CDATA[networking]]></category><category><![CDATA[cgnat]]></category><category><![CDATA[isp]]></category><category><![CDATA[RouterOS]]></category><category><![CDATA[troubleshooting]]></category><category><![CDATA[home networking]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 10 Aug 2025 03:02:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754794764641/a8f357fc-5a43-4b8c-9909-49f92f449273.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Module 5 of my MikroTik Zero to Hero Challenge</em></p>
<p>I started Module 5 thinking I'd learn some simple NAT rules and set up port forwarding. What I discovered instead was that most home networks don't work the way networking tutorials assume. Welcome to the world of double NAT!</p>
<h2 id="heading-the-assumption-vs-reality">The Assumption vs Reality</h2>
<p><strong>What I Thought My Network Looked Like:</strong></p>
<pre><code class="lang-bash">Internet (Public IP) → MikroTik → LAN devices
</code></pre>
<p><strong>What It Looked Like:</strong></p>
<pre><code class="lang-bash">Internet → ISP Router → MikroTik → LAN devices
         Public IP    Private IP   Private IP
</code></pre>
<p>This changes everything about how NAT and port forwarding work.</p>
<h2 id="heading-discovering-my-real-network-topology">Discovering My Real Network Topology</h2>
<p>When I investigated my actual setup, here's what I found:</p>
<ul>
<li><p><strong>Public IP</strong>: 102.219.209.38 (shared among ISP customers)</p>
</li>
<li><p><strong>ISP Router</strong>: Giving my MikroTik 192.168.100.40</p>
</li>
<li><p><strong>MikroTik WAN</strong>: 192.168.100.40 (private address!)</p>
</li>
<li><p><strong>MikroTik LAN</strong>: 192.168.88.0/24 (my controlled network)</p>
</li>
</ul>
<p><strong>Big revelation</strong>: My MikroTik doesn't have a real public IP address. It's getting a private IP from my ISP's router.</p>
<h2 id="heading-understanding-nat-in-simple-terms">Understanding NAT in Simple Terms</h2>
<p>Before diving into the complexity, let me explain what NAT does:</p>
<p><strong>The Problem</strong>: Private IP addresses (like 192.168.x.x) can't talk to the internet directly.</p>
<p><strong>The Solution</strong>: NAT translates private addresses to public addresses when packets leave your network, then translates back when replies come in.</p>
<h3 id="heading-two-types-of-nat">Two Types of NAT</h3>
<p><strong>Source NAT (SRCNAT/Masquerade)</strong>:</p>
<ul>
<li><p>Changes the source IP of outgoing packets</p>
</li>
<li><p>Enables internal devices to access the internet</p>
</li>
<li><p>Example: My laptop (192.168.88.200) appears as 192.168.100.40 to the ISP</p>
</li>
</ul>
<p><strong>Destination NAT (DSTNAT)</strong>:</p>
<ul>
<li><p>Changes the destination IP of incoming packets</p>
</li>
<li><p>Enables external access to internal services</p>
</li>
<li><p>Example: Traffic to 192.168.100.40:22 gets redirected to 192.168.88.200:22</p>
</li>
</ul>
<h2 id="heading-setting-up-internet-access-the-easy-part">Setting Up Internet Access (The Easy Part)</h2>
<p>Getting my LAN devices online was straightforward:</p>
<pre><code class="lang-bash">/ip firewall nat add chain=srcnat src-address=192.168.88.0/24 out-interface=ISP action=masquerade comment=<span class="hljs-string">"LAN to Internet"</span>
</code></pre>
<p>This rule says: "When any device from my LAN (192.168.88.x) sends traffic out through the ISP interface, change the source IP to match the interface IP (192.168.100.40)."</p>
<p><strong>Result</strong>: All my devices could browse the internet perfectly!</p>
<h2 id="heading-the-port-forwarding-reality-check">The Port Forwarding Reality Check</h2>
<p>Here's where my dreams got crushed. I wanted to set up SSH access from the internet to my internal server.</p>
<p>I configured the DSTNAT rule:</p>
<pre><code class="lang-bash">/ip firewall nat add chain=dstnat dst-address=192.168.100.40 dst-port=22 protocol=tcp action=dst-nat to-addresses=192.168.88.200 comment=<span class="hljs-string">"SSH port forwarding"</span>
</code></pre>
<p><strong>The Problem</strong>: This only works for traffic that reaches my MikroTik's WAN IP. But internet traffic hits my ISP router first, not my MikroTik!</p>
<h3 id="heading-why-true-port-forwarding-failed">Why True Port Forwarding Failed</h3>
<p>The traffic flow for external access looks like this:</p>
<pre><code class="lang-bash">Internet User → ISP Router (192.168.100.1) → [STOPS HERE]
</code></pre>
<p>The ISP router doesn't know how to forward port 22 traffic to my MikroTik at 192.168.100.40. I'd need to configure port forwarding on the ISP router too, but I don't have access to it.</p>
<h2 id="heading-hairpin-nat-a-clever-workaround">Hairpin NAT: A Clever Workaround</h2>
<p>Even though real port forwarding didn't work, I could test the concept using "hairpin NAT" - accessing my external IP from inside my network.</p>
<p>From my laptop, I could SSH to 192.168.100.40:22 and get connected to 192.168.88.200:22. The NAT rule worked perfectly for internal testing!</p>
<h2 id="heading-understanding-double-nat-impact">Understanding Double NAT Impact</h2>
<p>Most home internet connections work this way:</p>
<p><strong>Your ISP does NAT</strong>: Your public IP is shared among many customers<br /><strong>Your router does NAT</strong>: Your devices share your router's private IP<br /><strong>Result</strong>: Two layers of address translation</p>
<h3 id="heading-why-this-matters">Why This Matters</h3>
<p><strong>✅ What Works</strong>:</p>
<ul>
<li><p>Internet browsing and streaming</p>
</li>
<li><p>Most apps and services</p>
</li>
<li><p>Internal network communication</p>
</li>
</ul>
<p><strong>❌ What Doesn't Work</strong>:</p>
<ul>
<li><p>Hosting public services (web servers, game servers)</p>
</li>
<li><p>Some peer-to-peer applications</p>
</li>
<li><p>Direct external access to internal devices</p>
</li>
</ul>
<h2 id="heading-the-difference-between-consumer-and-business-internet">The Difference Between Consumer and Business Internet</h2>
<p><strong>Consumer/Residential Internet</strong>:</p>
<ul>
<li><p>Shared public IP addresses (CGNAT)</p>
</li>
<li><p>Limited or no port forwarding capability</p>
</li>
<li><p>Designed for consuming content, not hosting</p>
</li>
</ul>
<p><strong>Business Internet</strong>:</p>
<ul>
<li><p>Dedicated public IP addresses</p>
</li>
<li><p>Full control over port forwarding</p>
</li>
<li><p>Designed for hosting services and servers</p>
</li>
</ul>
<p><strong>The Upgrade Path</strong>: Business internet plans if you need to host public services.</p>
<h2 id="heading-what-i-accomplished">What I Accomplished</h2>
<p>Even with double NAT limitations, I achieved:</p>
<p>✅ <strong>Perfect internet access</strong> - All LAN devices are browsing normally<br />✅ <strong>Clean NAT configuration</strong> - Proper masquerade rules<br />✅ <strong>Internal port forwarding</strong> - Working for local testing<br />✅ <strong>Network architecture understanding</strong> - Real-world vs textbook differences</p>
<h2 id="heading-key-commands-for-nat">Key Commands for NAT</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># View current NAT rules</span>
/ip firewall nat <span class="hljs-built_in">print</span>

<span class="hljs-comment"># Remove all NAT rules (clean slate)</span>
/ip firewall nat remove [find]

<span class="hljs-comment"># Source NAT for internet access  </span>
/ip firewall nat add chain=srcnat src-address=192.168.88.0/24 out-interface=ISP action=masquerade

<span class="hljs-comment"># Destination NAT for port forwarding</span>
/ip firewall nat add chain=dstnat dst-address=WAN-IP dst-port=PORT protocol=tcp action=dst-nat to-addresses=INTERNAL-IP

<span class="hljs-comment"># Monitor active connections</span>
/ip firewall connection <span class="hljs-built_in">print</span>
</code></pre>
<h2 id="heading-the-reality-of-home-networking">The Reality of Home Networking</h2>
<p>Module 5 taught me that <strong>textbook networking and real-world networking are often different</strong>.</p>
<p>Most networking tutorials assume you have:</p>
<ul>
<li><p>A direct internet connection</p>
</li>
<li><p>A real public IP address</p>
</li>
<li><p>Full control over port forwarding</p>
</li>
</ul>
<p>But most home users have:</p>
<ul>
<li><p>Connection through ISP equipment</p>
</li>
<li><p>Shared/private IP addresses from ISPs</p>
</li>
<li><p>Limited hosting capabilities</p>
</li>
</ul>
<p>This isn't a failure - it's just reality. Understanding these constraints helps you work within them instead of fighting against them.</p>
<h2 id="heading-what-nat-doesnt-do">What NAT Doesn't Do</h2>
<p>Important clarification: <strong>NAT only handles IP address translation</strong>. It doesn't:</p>
<ul>
<li><p>Block websites or content (that's firewall filtering)</p>
</li>
<li><p>Control application access (that's layer 7 filtering)</p>
</li>
<li><p>Manage bandwidth (that's Quality of Service)</p>
</li>
<li><p>Provide security (that's firewall rules)</p>
</li>
</ul>
<p>Each network function has its tools and rules.</p>
<h2 id="heading-from-frustration-to-understanding">From Frustration to Understanding</h2>
<p>Initially, I was frustrated that "simple" port forwarding didn't work. But learning about double NAT, CGNAT, and ISP network architecture gave me a much deeper understanding of how the internet really works.</p>
<p>This knowledge is valuable whether you're troubleshooting home networks, planning business infrastructure, or just understanding why some applications work better than others.</p>
<p>The journey was teaching me that <strong>good networking isn't about memorising commands - it's about understanding how traffic flows through complex, real-world infrastructures</strong>.</p>
<hr />
<p><em>This is part of my MikroTik Zero to Hero challenge. Sometimes the most valuable lessons come from discovering what doesn't work and why.</em></p>
<p><strong>Next up</strong>: Module 6 - Wireless Configuration (Building secure WiFi networks with proper isolation)</p>
]]></content:encoded></item><item><title><![CDATA[From Zero Security to Enterprise-Grade Protection: My First MikroTik Firewall Setup]]></title><description><![CDATA[Module 4 of my MikroTik Zero to Hero Challenge
After three modules of getting basic connectivity working, I had a sobering realisation: my router was completely naked on the internet. No firewall rules. Management interfaces are accessible from anywh...]]></description><link>https://blog.lxmwaniky.me/mikrotik-firewall-zero-to-enterprise</link><guid isPermaLink="true">https://blog.lxmwaniky.me/mikrotik-firewall-zero-to-enterprise</guid><category><![CDATA[#Mikrotik]]></category><category><![CDATA[networking]]></category><category><![CDATA[routing]]></category><category><![CDATA[firewall]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 10 Aug 2025 02:52:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754794214849/ca391629-735d-4e3c-a905-7b91e1bc964f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Module 4 of my MikroTik Zero to Hero Challenge</em></p>
<p>After three modules of getting basic connectivity working, I had a sobering realisation: my router was completely naked on the internet. No firewall rules. Management interfaces are accessible from anywhere. Time for a serious security wake-up call.</p>
<h2 id="heading-the-scary-reality-check">The Scary Reality Check</h2>
<p>Picture this: Your router is humming along nicely, DHCP is working, the internet is flowing smoothly, and then you realise <strong>anyone on the internet can try to access your router's management interface</strong>.</p>
<p>That's exactly where I was after Module 3. My network worked great, but it was about as secure as leaving your front door wide open with a "Welcome Hackers" sign.</p>
<h2 id="heading-understanding-how-traffic-flows">Understanding How Traffic Flows</h2>
<p>Before jumping into firewall rules, I had to understand where traffic goes in my network. This was the key to everything.</p>
<h3 id="heading-the-three-traffic-paths">The Three Traffic Paths</h3>
<p><strong>1. INPUT Chain - "Who can talk TO the router?"</strong></p>
<ul>
<li><p>When I use Winbox to manage the router</p>
</li>
<li><p>When someone tries to access the router from the internet (bad!)</p>
</li>
<li><p>When the router receives replies from internet services</p>
</li>
</ul>
<p><strong>2. FORWARD Chain - "Who can pass THROUGH the router?"</strong></p>
<ul>
<li><p>When my laptop browses the internet</p>
</li>
<li><p>When someone from the internet tries to reach my laptop (also bad!)</p>
</li>
<li><p>Most of your normal internet traffic</p>
</li>
</ul>
<p><strong>3. OUTPUT Chain - "What can the router send OUT?"</strong></p>
<ul>
<li><p>When the router updates its time via NTP</p>
</li>
<li><p>When the router checks for software updates</p>
</li>
<li><p>Usually not configured in basic setups</p>
</li>
</ul>
<p>Here's how it looks:</p>
<pre><code class="lang-bash">Laptop → Internet: Goes through FORWARD chain
Managing router: Goes through INPUT chain  
Router getting updates: Goes through OUTPUT chain
</code></pre>
<h2 id="heading-the-magic-of-connection-states">The Magic of Connection States</h2>
<p>This blew my mind. Instead of looking at every packet individually, MikroTik's firewall remembers connection states:</p>
<ul>
<li><p><strong>NEW</strong>: Someone is trying to start a new connection</p>
</li>
<li><p><strong>ESTABLISHED</strong>: Packets from connections we already started</p>
</li>
<li><p><strong>RELATED</strong>: Traffic related to connections we have (like FTP data)</p>
</li>
<li><p><strong>INVALID</strong>: Weird, suspicious, or broken packets</p>
</li>
</ul>
<p><strong>The security superpower</strong>: Allow replies to connections we started, but block strangers trying to start new connections to us.</p>
<h2 id="heading-building-my-first-real-security">Building My First Real Security</h2>
<h3 id="heading-securing-the-router-itself-input-chain">Securing the Router Itself (INPUT Chain)</h3>
<p>I started with the most critical part - protecting the router from internet attacks:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Rule 1: Allow replies to connections the router started</span>
/ip firewall filter add chain=input connection-state=established,related action=accept comment=<span class="hljs-string">"Allow replies to my connections"</span>

<span class="hljs-comment"># Rule 2: Drop suspicious/broken traffic</span>
/ip firewall filter add chain=input connection-state=invalid action=drop comment=<span class="hljs-string">"Drop invalid packets"</span>

<span class="hljs-comment"># Rule 3: Allow ping from my local network only</span>
/ip firewall filter add chain=input protocol=icmp src-address=192.168.88.0/24 action=accept comment=<span class="hljs-string">"Allow ping from LAN"</span>

<span class="hljs-comment"># Rule 4: Allow management from my local network only  </span>
/ip firewall filter add chain=input src-address=192.168.88.0/24 dst-port=8291 protocol=tcp action=accept comment=<span class="hljs-string">"Winbox from LAN"</span>

<span class="hljs-comment"># Rule 5: Block everything else</span>
/ip firewall filter add chain=input action=drop comment=<span class="hljs-string">"Drop all other input"</span>
</code></pre>
<h3 id="heading-controlling-internet-access-forward-chain">Controlling Internet Access (FORWARD Chain)</h3>
<p>Next, I controlled what could pass through the router:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Rule 1: Allow replies to connections my devices started</span>
/ip firewall filter add chain=forward connection-state=established,related action=accept comment=<span class="hljs-string">"Allow reply traffic"</span>

<span class="hljs-comment"># Rule 2: Allow my local network to access the internet</span>
/ip firewall filter add chain=forward src-address=192.168.88.0/24 action=accept comment=<span class="hljs-string">"Allow LAN to internet"</span>

<span class="hljs-comment"># Rule 3: Drop suspicious forwarded traffic  </span>
/ip firewall filter add chain=forward connection-state=invalid action=drop comment=<span class="hljs-string">"Drop invalid forward"</span>

<span class="hljs-comment"># Rule 4: Block everything else from getting to my network</span>
/ip firewall filter add chain=forward action=drop comment=<span class="hljs-string">"Drop all other forward"</span>
</code></pre>
<h2 id="heading-the-moment-i-locked-myself-out">The Moment I Locked Myself Out</h2>
<p>Here's where things got interesting. I was connected to my router through my home WiFi (not the lab network) when I enabled the firewall.</p>
<p><strong>Suddenly, no more access!</strong></p>
<p>The firewall was working perfectly - it was blocking my WiFi connection because it wasn't from the trusted LAN network (192.168.88.0/24).</p>
<h3 id="heading-creative-problem-solving">Creative Problem Solving</h3>
<p>I had to add a special rule to allow my wireless connection:</p>
<pre><code class="lang-bash">/ip firewall filter add chain=input src-address=MY-WIRELESS-IP dst-port=8291 protocol=tcp action=accept comment=<span class="hljs-string">"Winbox from wireless"</span> place-before=4
</code></pre>
<p>The <code>place-before=4</code> was crucial - it had to go before the "drop everything else" rule.</p>
<h2 id="heading-testing-my-security">Testing My Security</h2>
<p>I did proper security testing:</p>
<p><strong>✅ Positive Test</strong>: My laptop from the LAN could still manage the router<br /><strong>✅ Negative Test</strong>: A different device couldn't access management<br /><strong>✅ Functionality Test</strong>: Internet access still worked perfectly</p>
<p>All tests passed! The firewall was doing exactly what it should.</p>
<h2 id="heading-understanding-rule-order-super-important">Understanding Rule Order (Super Important!)</h2>
<p>MikroTik processes firewall rules from top to bottom and stops at the first match. This means <strong>order matters a lot</strong>.</p>
<p>I made the mistake of adding duplicate rules by running commands multiple times. Had to clean up:</p>
<pre><code class="lang-bash">/ip firewall filter remove 0,1,2,3,4
</code></pre>
<p><strong>Lesson learned</strong>: Always check existing rules before adding new ones.</p>
<h2 id="heading-what-my-security-policy-does">What My Security Policy Does</h2>
<p><strong>For Management Access</strong>:</p>
<ul>
<li><p>✅ Allow from my local network (192.168.88.0/24)</p>
</li>
<li><p>✅ Allow from my specific wireless IP</p>
</li>
<li><p>❌ Block everyone else on the internet</p>
</li>
</ul>
<p><strong>For Internet Access</strong>:</p>
<ul>
<li><p>✅ My devices can browse the internet freely</p>
</li>
<li><p>✅ Replies to my requests come back fine</p>
</li>
<li><p>❌ Random internet users can't reach my devices</p>
</li>
</ul>
<p><strong>For Network Diagnostics</strong>:</p>
<ul>
<li><p>✅ I can ping the router from my network</p>
</li>
<li><p>❌ Internet users can't ping my router</p>
</li>
</ul>
<h2 id="heading-the-commands-that-matter">The Commands That Matter</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># View all firewall rules</span>
/ip firewall filter <span class="hljs-built_in">print</span>

<span class="hljs-comment"># Add a rule at a specific position</span>
/ip firewall filter add chain=input src-address=192.168.88.0/24 dst-port=8291 protocol=tcp action=accept place-before=5

<span class="hljs-comment"># Remove rules (be careful!)</span>
/ip firewall filter remove 0,1,2

<span class="hljs-comment"># Test connectivity</span>
/ping 8.8.8.8
/ip firewall connection <span class="hljs-built_in">print</span>
</code></pre>
<h2 id="heading-what-i-achieved">What I Achieved</h2>
<p>By the end of Module 4:</p>
<p>✅ <strong>Router is secure</strong> - Management only from trusted networks<br />✅ <strong>Internet access maintained</strong> - Users can browse normally<br />✅ <strong>Attack resistance</strong> - Invalid and suspicious traffic blocked<br />✅ <strong>Diagnostic capability</strong> - Network troubleshooting still works<br />✅ <strong>Professional security model</strong> - Default deny with explicit allows</p>
<h2 id="heading-the-big-learning">The Big Learning</h2>
<p>Module 4 taught me that <strong>good security is about controlling traffic flows, not just blocking bad stuff</strong>.</p>
<p>The connection state tracking was the real game-changer. Instead of trying to guess what's good or bad, I let the firewall remember what connections my network started, and only allow replies to those.</p>
<p>This is how enterprise networks work - not by trying to identify every possible threat, but by having a clear policy about what's allowed and blocking everything else.</p>
<h2 id="heading-from-open-door-to-fort-knox">From Open Door to Fort Knox</h2>
<p>My network went from being completely open to having enterprise-grade security. But this was just the foundation. I still had no NAT rules for internet access, no VLANs for network segmentation, and no Quality of Service controls.</p>
<p>The journey was getting more complex, but also more powerful. I was starting to understand why network professionals choose MikroTik - it gives you complete control to build exactly what you need.</p>
<hr />
<p><em>This is part of my MikroTik Zero to Hero challenge. Security first, everything else follows.</em></p>
<p><strong>Next up</strong>: Module 5 - NAT &amp; Port Forwarding (Making internal services accessible while staying secure)</p>
]]></content:encoded></item><item><title><![CDATA[Day 2 Morning: Finally Getting Automatic IP Addresses Working]]></title><description><![CDATA[Module 3 of my MikroTik Zero to Hero Challenge
I started Day 2 with the internet working perfectly - but only if I manually set IP addresses on my devices. Time to fix this and make the network usable for normal people who just want to plug in and go...]]></description><link>https://blog.lxmwaniky.me/getting-automatic-ip-addresses</link><guid isPermaLink="true">https://blog.lxmwaniky.me/getting-automatic-ip-addresses</guid><category><![CDATA[dhcp server]]></category><category><![CDATA[#Mikrotik]]></category><category><![CDATA[ip address]]></category><category><![CDATA[routing]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 10 Aug 2025 02:45:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754793835808/86474101-a8b3-4dba-9715-4cba0f24ac0b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Module 3 of my MikroTik Zero to Hero Challenge</em></p>
<p>I started Day 2 with the internet working perfectly - but only if I manually set IP addresses on my devices. Time to fix this and make the network usable for normal people who just want to plug in and go online.</p>
<h2 id="heading-the-starting-problem">The Starting Problem</h2>
<p>My laptop was still showing <code>169.254.86.20</code> - that annoying self-assigned IP address Windows gives itself when it can't find a DHCP server.</p>
<p><strong>The Issue</strong>: My router could get online, but it wasn't helping other devices get IP addresses automatically.</p>
<p><strong>The Goal</strong>: Make it work like a normal home router where devices just connect and everything works.</p>
<h2 id="heading-understanding-dhcp-components">Understanding DHCP Components</h2>
<p>Before diving in, I had to learn what pieces make DHCP work:</p>
<ul>
<li><p><strong>IP Pool</strong>: The range of addresses available to give out (like 192.168.88.100 to 192.168.88.200)</p>
</li>
<li><p><strong>DHCP Server</strong>: The service that hands out IP addresses</p>
</li>
<li><p><strong>DHCP Network</strong>: The configuration tells clients what gateway and DNS servers to use</p>
</li>
<li><p><strong>DHCP Lease</strong>: The record of who has which IP address</p>
</li>
</ul>
<p>Think of it like a hotel check-in system - you need rooms available (pool), a front desk person (server), information about hotel services (network config), and a guest registry (leases).</p>
<h2 id="heading-setting-up-my-first-dhcp-server">Setting Up My First DHCP Server</h2>
<h3 id="heading-step-1-create-the-ip-pool">Step 1: Create the IP Pool</h3>
<pre><code class="lang-bash">/ip pool add name=LAN-Pool ranges=192.168.88.100-192.168.88.200
</code></pre>
<p>This gives me 101 IP addresses to hand out (100-200), while keeping 1-99 free for servers and network equipment.</p>
<h3 id="heading-step-2-create-the-dhcp-server">Step 2: Create the DHCP Server</h3>
<pre><code class="lang-bash">/ip dhcp-server add name=LAN-DHCP interface=Bridge-LAN address-pool=LAN-Pool
</code></pre>
<p>Simple enough! Or so I thought...</p>
<h3 id="heading-step-3-configure-network-settings">Step 3: Configure Network Settings</h3>
<pre><code class="lang-bash">/ip dhcp-server network add address=192.168.88.0/24 gateway=192.168.88.1 dns-server=8.8.8.8,1.1.1.1
</code></pre>
<p>This tells DHCP clients:</p>
<ul>
<li><p>Your network is 192.168.88.0/24</p>
</li>
<li><p>Your gateway (router) is 192.168.88.1</p>
</li>
<li><p>Use Google (8.8.8.8) and Cloudflare (1.1.1.1) for DNS</p>
</li>
</ul>
<h2 id="heading-the-hidden-problem-i-discovered">The Hidden Problem I Discovered</h2>
<p>After setting everything up, my laptop was STILL getting <code>169.254.x.x</code>!</p>
<p>I checked the DHCP server status:</p>
<pre><code class="lang-bash">/ip dhcp-server <span class="hljs-built_in">print</span>
</code></pre>
<p>And there was the problem - it showed an <strong>X</strong> flag, meaning the server was disabled!</p>
<p><strong>Big Learning</strong>: MikroTik creates DHCP servers in a disabled state for safety. You have to manually enable them.</p>
<pre><code class="lang-bash">/ip dhcp-server <span class="hljs-built_in">enable</span> LAN-DHCP
</code></pre>
<p><strong>SUCCESS!</strong> My laptop immediately got <code>192.168.88.200</code>!</p>
<h2 id="heading-making-sure-my-laptop-always-gets-the-same-ip">Making Sure My Laptop Always Gets the Same IP</h2>
<p>For my lab work, I wanted my laptop to always get the same IP address. This is called a DHCP reservation.</p>
<p>First, I found my laptop's lease:</p>
<pre><code class="lang-bash">/ip dhcp-server lease <span class="hljs-built_in">print</span>
<span class="hljs-comment"># Showed: 192.168.88.200, MAC F4:30:B9:13:C1:55, status: bound</span>
</code></pre>
<p>Then I made it permanent:</p>
<pre><code class="lang-bash">/ip dhcp-server lease make-static [find mac-address=F4:30:B9:13:C1:55]
</code></pre>
<p>I got an error saying "already have static lease with this IP address," - but this meant it worked! Sometimes MikroTik error messages are confusing like that.</p>
<h2 id="heading-setting-up-time-synchronisation-ntp">Setting Up Time Synchronisation (NTP)</h2>
<p>Networks need accurate time for logging, certificates, and troubleshooting. Time to set up NTP (Network Time Protocol).</p>
<h3 id="heading-my-first-attempt-failed">My First Attempt (Failed)</h3>
<pre><code class="lang-bash">/system ntp client servers add address=pool.ntp.org
</code></pre>
<p><strong>Error</strong>: "bad command name servers"</p>
<p>Turns out my RouterOS version uses a different syntax.</p>
<h3 id="heading-second-attempt-also-failed">Second Attempt (Also Failed)</h3>
<pre><code class="lang-bash">/system ntp client <span class="hljs-built_in">set</span> primary-ntp=pool.ntp.org
</code></pre>
<p><strong>Error</strong>: "invalid value for argument ip-address"</p>
<p><strong>Learning</strong>: NTP needs actual IP addresses, not domain names like "<a target="_blank" href="http://pool.ntp.org">pool.ntp.org</a>".</p>
<h3 id="heading-what-worked">What Worked</h3>
<pre><code class="lang-bash">/system ntp client <span class="hljs-built_in">set</span> primary-ntp=129.6.15.28
/system ntp client <span class="hljs-built_in">set</span> secondary-ntp=132.163.97.1
</code></pre>
<p>I looked up the IP addresses for reliable NTP servers and used those directly.</p>
<p>Checking if it worked:</p>
<pre><code class="lang-bash">/system ntp client <span class="hljs-built_in">print</span>
<span class="hljs-comment"># Results:</span>
<span class="hljs-comment"># active-server: 129.6.15.28</span>
<span class="hljs-comment"># last-update-before: 7s450ms  </span>
<span class="hljs-comment"># last-adjustment: 15ms384us</span>
</code></pre>
<p>Perfect! The router was now keeping accurate time.</p>
<h2 id="heading-watching-the-system-in-action">Watching the System in Action</h2>
<p>MikroTik has great logging. I could watch DHCP in action:</p>
<pre><code class="lang-bash">/<span class="hljs-built_in">log</span> <span class="hljs-built_in">print</span> <span class="hljs-built_in">where</span> topics~<span class="hljs-string">"dhcp"</span>
</code></pre>
<p>This showed me every time a device requested an IP address, got one, or renewed its lease. Helpful for troubleshooting!</p>
<h2 id="heading-what-i-accomplished-in-module-3">What I Accomplished in Module 3</h2>
<p>By the end of Module 3, I had a fully functional network:</p>
<p>✅ <strong>DHCP server working</strong> - Devices automatically get IP addresses<br />✅ <strong>IP pool configured</strong> - 192.168.88.100-200 range for automatic assignment<br />✅ <strong>Static reservation</strong> - My laptop always gets 192.168.88.200<br />✅ <strong>DNS servers configured</strong> - Clients get Google and Cloudflare DNS automatically<br />✅ <strong>Time synchronisation</strong> - Router keeps accurate time via NTP<br />✅ <strong>Network monitoring</strong> - Can watch DHCP activity in real-time</p>
<h2 id="heading-the-complete-picture">The Complete Picture</h2>
<p>Now, when someone plugs into my network, they automatically get:</p>
<ul>
<li><p>An IP address (192.168.88.100-200)</p>
</li>
<li><p>Subnet mask (255.255.255.0)</p>
</li>
<li><p>Gateway address (192.168.88.1)</p>
</li>
<li><p>DNS servers (8.8.8.8 and 1.1.1.1)</p>
</li>
</ul>
<p>No manual configuration needed - just like a professional network!</p>
<h2 id="heading-key-commands-i-learned">Key Commands I Learned</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># DHCP Server Setup</span>
/ip pool add name=LAN-Pool ranges=192.168.88.100-192.168.88.200
/ip dhcp-server add name=LAN-DHCP interface=Bridge-LAN address-pool=LAN-Pool  
/ip dhcp-server network add address=192.168.88.0/24 gateway=192.168.88.1 dns-server=8.8.8.8,1.1.1.1
/ip dhcp-server <span class="hljs-built_in">enable</span> LAN-DHCP

<span class="hljs-comment"># DHCP Management  </span>
/ip dhcp-server <span class="hljs-built_in">print</span>
/ip dhcp-server lease <span class="hljs-built_in">print</span>
/ip dhcp-server lease make-static [find mac-address=MAC-ADDRESS]

<span class="hljs-comment"># NTP Time Sync</span>
/system ntp client <span class="hljs-built_in">set</span> enabled=yes
/system ntp client <span class="hljs-built_in">set</span> primary-ntp=129.6.15.28
/system ntp client <span class="hljs-built_in">print</span>

<span class="hljs-comment"># Monitoring</span>
/<span class="hljs-built_in">log</span> <span class="hljs-built_in">print</span> <span class="hljs-built_in">where</span> topics~<span class="hljs-string">"dhcp"</span>
</code></pre>
<h2 id="heading-what-i-learned-about-routeros">What I Learned About RouterOS</h2>
<ol>
<li><p><strong>Services start disabled</strong> - Always check if you need to enable things</p>
</li>
<li><p><strong>Error messages can be confusing</strong> - Sometimes they mean success!</p>
</li>
<li><p><strong>Domain names don't always work</strong> - Some services need IP addresses</p>
</li>
<li><p><strong>Logging is your friend</strong> - Use it to see what's happening</p>
</li>
<li><p><strong>MikroTik doesn't guess</strong> - You have to be explicit about everything</p>
</li>
</ol>
<h2 id="heading-from-frustration-to-understanding">From Frustration to Understanding</h2>
<p>Module 3 was where things started feeling "normal." Instead of fighting the system, I was beginning to understand its logic.</p>
<p>The network was finally behaving like people expect - plug in a device, and it just works. But I knew this was still just the beginning. So far, my network has had zero security. Anyone could connect and do anything.</p>
<p>Time to fix that...</p>
<hr />
<p><em>This is part of my MikroTik Zero to Hero challenge. We're building toward a secure, professional network step by step.</em></p>
<p><strong>Next up</strong>: Module 4 - Firewall Fundamentals (Time to lock things down!)</p>
]]></content:encoded></item><item><title><![CDATA[The /24 vs. /0 Mistake: A MikroTik Routing Lesson]]></title><description><![CDATA[Module 2 of my MikroTik Zero to Hero Challenge
After getting familiar with MikroTik in the morning, I thought the afternoon would be easy - just set up some IP addresses and get online. I was so wrong.
The Big Problem I Discovered
Remember that bridg...]]></description><link>https://blog.lxmwaniky.me/mikrotik-24-vs-0-routing-lesson</link><guid isPermaLink="true">https://blog.lxmwaniky.me/mikrotik-24-vs-0-routing-lesson</guid><category><![CDATA[#Mikrotik]]></category><category><![CDATA[networking]]></category><category><![CDATA[subnetting]]></category><category><![CDATA[routing]]></category><category><![CDATA[dhcp]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 10 Aug 2025 00:28:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754785548072/130b3154-6899-4386-9fd2-40ef5e4d4a6e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Module 2 of my MikroTik Zero to Hero Challenge</em></p>
<p>After getting familiar with MikroTik in the morning, I thought the afternoon would be easy - just set up some IP addresses and get online. I was so wrong.</p>
<h2 id="heading-the-big-problem-i-discovered">The Big Problem I Discovered</h2>
<p>Remember that <code>bridgeLocal</code> from Module 1? Well, it was causing a huge mess:</p>
<ul>
<li><p>All 4 Ethernet ports were acting like one big switch</p>
</li>
<li><p>My internet connection (WAN) was mixed with my local network (LAN)</p>
</li>
<li><p>My laptop was getting a weird IP address: <code>169.254.86.20</code></p>
</li>
</ul>
<p>That <code>169.254.x.x</code> address is what Windows gives itself when it can't find a DHCP server. Not good!</p>
<h2 id="heading-separating-wan-and-lan-the-right-way">Separating WAN and LAN (The Right Way)</h2>
<p>First thing I had to do was separate the internet connection from my local network:</p>
<p><strong>The Problem</strong>: Everything was on one bridge<br /><strong>The Solution</strong>: Create proper separation</p>
<p>Here's what I did:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Delete the messy bridge</span>
/interface bridge remove bridgeLocal

<span class="hljs-comment"># Create a new bridge for LAN only</span>
/interface bridge add name=Bridge-LAN

<span class="hljs-comment"># Put only the local ports in the LAN bridge</span>
/interface bridge port add interface=ether2 bridge=Bridge-LAN
/interface bridge port add interface=ether3 bridge=Bridge-LAN
/interface bridge port add interface=ether4 bridge=Bridge-LAN

<span class="hljs-comment"># Leave ether1 separate for internet connection</span>
<span class="hljs-comment"># Rename interfaces to make sense</span>
/interface <span class="hljs-built_in">set</span> ether1 name=ISP
/interface <span class="hljs-built_in">set</span> ether2 name=Laptop
</code></pre>
<p>Now I had a clean separation:</p>
<ul>
<li><p><code>ISP</code> (ether1) = Internet connection</p>
</li>
<li><p><code>Bridge-LAN</code> (ether2-4) = Local network</p>
</li>
</ul>
<h2 id="heading-setting-up-ip-addresses">Setting Up IP Addresses</h2>
<p>Time to give everything proper IP addresses:</p>
<p><strong>WAN Side (Internet)</strong>: Use DHCP to get an IP from my ISP router</p>
<pre><code class="lang-bash">/ip dhcp-client add interface=ISP
</code></pre>
<p><strong>LAN Side (Local Network)</strong>: Use static IP</p>
<pre><code class="lang-bash">/ip address add address=192.168.88.1/24 interface=Bridge-LAN
</code></pre>
<p>The <code>/24</code> means the first 24 bits are the network part. So <code>192.168.88.1/24</code> means:</p>
<ul>
<li><p>Router IP: 192.168.88.1</p>
</li>
<li><p>Network: 192.168.88.0 to 192.168.88.255</p>
</li>
</ul>
<h2 id="heading-the-big-mistake-that-taught-me-everything">The Big Mistake That Taught Me Everything</h2>
<p>After setting up IP addresses, I tried to ping Google:</p>
<pre><code class="lang-bash">/ping 8.8.8.8
</code></pre>
<p><strong>FAILED!</strong></p>
<p>I could ping my ISP router (192.168.100.1), but not the internet. What was wrong?</p>
<p>I checked my routing table:</p>
<pre><code class="lang-bash">/ip route <span class="hljs-built_in">print</span>
</code></pre>
<p>And there it was - the problem:</p>
<pre><code class="lang-bash">0 A S  0.0.0.0/24     192.168.100.1
</code></pre>
<p>Do you see it? <strong>0.0.0.0/24</strong> instead of <strong>0.0.0.0/0</strong></p>
<h2 id="heading-the-24-vs-0-lesson">The /24 vs /0 Lesson</h2>
<p>This was my biggest learning moment:</p>
<ul>
<li><p><strong>0.0.0.0/24</strong> = Only route traffic for 0.0.0.1 to 0.0.0.255 (useless!)</p>
</li>
<li><p><strong>0.0.0.0/0</strong> = Route ALL unknown traffic to this gateway (what we want!)</p>
</li>
</ul>
<p>The fix:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Remove the wrong route</span>
/ip route remove [find dst-address=0.0.0.0/24]

<span class="hljs-comment"># Add the correct default route</span>
/ip route add dst-address=0.0.0.0/0 gateway=192.168.100.1
</code></pre>
<p><strong>SUCCESS!</strong> Now I could ping 8.8.8.8!</p>
<h2 id="heading-another-mistake-i-made">Another Mistake I Made</h2>
<p>I accidentally added a DHCP client to my LAN bridge:</p>
<pre><code class="lang-bash">/ip dhcp-client add interface=Bridge-LAN  <span class="hljs-comment"># WRONG!</span>
</code></pre>
<p>This made no sense because:</p>
<ul>
<li><p><strong>WAN interfaces</strong> should GET IP addresses (be DHCP clients)</p>
</li>
<li><p><strong>LAN interfaces</strong> should GIVE IP addresses (be DHCP servers)</p>
</li>
</ul>
<p>I was asking my LAN to search for a DHCP server that didn't exist!</p>
<h2 id="heading-understanding-my-network-layout">Understanding My Network Layout</h2>
<p>By the end of Module 2, my network looked like this:</p>
<pre><code class="lang-bash">Internet → ISP Router → ether1(ISP) → MikroTik → Bridge-LAN → ether2-4
           192.168.100.1  192.168.100.40  192.168.88.1
</code></pre>
<p>Clean and logical!</p>
<h2 id="heading-what-i-learned-about-routing">What I Learned About Routing</h2>
<p>The routing table shows how traffic gets around:</p>
<pre><code class="lang-bash">/ip route <span class="hljs-built_in">print</span>
<span class="hljs-comment"># Results:</span>
<span class="hljs-comment"># 0 A S  0.0.0.0/0       192.168.100.1  (send everything unknown here)</span>
<span class="hljs-comment"># 1 ADC  192.168.88.0/24 Bridge-LAN     (local network is directly connected)  </span>
<span class="hljs-comment"># 2 ADC  192.168.100.0/24 ISP           (ISP network is directly connected)</span>
</code></pre>
<p>The flags mean:</p>
<ul>
<li><p><strong>A</strong> = Active (route is working)</p>
</li>
<li><p><strong>D</strong> = Dynamic (created automatically)</p>
</li>
<li><p><strong>C</strong> = Connected (directly attached network)</p>
</li>
<li><p><strong>S</strong> = Static (I created this manually)</p>
</li>
</ul>
<h2 id="heading-my-troubleshooting-method">My Troubleshooting Method</h2>
<p>When things don't work, I learned to check in this order:</p>
<ol>
<li><p><strong>Symptoms</strong>: Can't reach 8.8.8.8</p>
</li>
<li><p><strong>Check routing</strong>: <code>/ip route print</code></p>
</li>
<li><p><strong>Check interfaces</strong>: <code>/ip address print</code></p>
</li>
<li><p><strong>Test step by step</strong>: Local → Gateway → Internet</p>
</li>
<li><p><strong>Fix the root cause</strong>: Correct the subnet mask</p>
</li>
</ol>
<h2 id="heading-what-i-accomplished">What I Accomplished</h2>
<p>By the end of Module 2:</p>
<p>✅ <strong>Proper WAN/LAN separation</strong> - No more mixed networks<br />✅ <strong>Internet connectivity working</strong> - Can ping 8.8.8.8<br />✅ <strong>Clean routing table</strong> - Correct default route<br />✅ <strong>Logical interface naming</strong> - ISP and Laptop instead of ether1/ether2<br />✅ <strong>Understanding traffic flow</strong> - Know how packets move around</p>
<h2 id="heading-the-one-thing-still-not-working">The One Thing Still Not Working</h2>
<p>My laptop was still getting that <code>169.254.x.x</code> address. I could manually set a static IP and everything worked, but automatic assignment wasn't happening yet.</p>
<p><strong>Next challenge</strong>: Set up a DHCP server so devices get IP addresses automatically.</p>
<h2 id="heading-key-commands-i-mastered">Key Commands I Mastered</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># IP address management</span>
/ip address add address=192.168.88.1/24 interface=Bridge-LAN
/ip address <span class="hljs-built_in">print</span>

<span class="hljs-comment"># DHCP client management  </span>
/ip dhcp-client add interface=ISP
/ip dhcp-client <span class="hljs-built_in">print</span>

<span class="hljs-comment"># Routing</span>
/ip route <span class="hljs-built_in">print</span>
/ip route add dst-address=0.0.0.0/0 gateway=192.168.100.1

<span class="hljs-comment"># Testing connectivity</span>
/ping 8.8.8.8 count=3
/ping 192.168.100.1
</code></pre>
<h2 id="heading-the-real-learning">The Real Learning</h2>
<p>Module 2 taught me that <strong>networking is all about logical separation</strong>. Just because ports are physically next to each other doesn't mean they should be on the same network.</p>
<p>The <code>/24 vs /0</code> mistake was embarrassing but incredibly valuable. Now I'll never forget that subnet masks completely change how routing works.</p>
<hr />
<p><em>This is part of my MikroTik Zero to Hero challenge. The journey from confusion to clarity continues!</em></p>
<p><strong>Next up</strong>: Module 3 - DHCP Server &amp; Basic Services (Finally getting automatic IP addresses working!)</p>
]]></content:encoded></item><item><title><![CDATA[Day 1: My First Time Using MikroTik RouterOS]]></title><description><![CDATA[Starting my MikroTik Zero to Hero Challenge - Module 1
I decided to learn MikroTik networking from scratch. This is my story of Day 1 - the very first time I touched a MikroTik router.
What is MikroTik Anyway?
Before starting, I had no idea what Mikr...]]></description><link>https://blog.lxmwaniky.me/my-first-time-using-mikrotik-routeros</link><guid isPermaLink="true">https://blog.lxmwaniky.me/my-first-time-using-mikrotik-routeros</guid><category><![CDATA[#Mikrotik]]></category><category><![CDATA[RouterOS]]></category><category><![CDATA[networking]]></category><category><![CDATA[networking for beginners]]></category><dc:creator><![CDATA[Alex Nyambura]]></dc:creator><pubDate>Sun, 10 Aug 2025 00:12:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754784669140/6c2f56ab-bbf5-4361-bd04-3d08cbc73a9c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Starting my MikroTik Zero to Hero Challenge - Module 1</em></p>
<p>I decided to learn MikroTik networking from scratch. This is my story of Day 1 - the very first time I touched a MikroTik router.</p>
<h2 id="heading-what-is-mikrotik-anyway">What is MikroTik Anyway?</h2>
<p>Before starting, I had no idea what MikroTik was. Here's what I learned:</p>
<ul>
<li><p><strong>MikroTik</strong> makes networking equipment (routers, switches, wireless devices)</p>
</li>
<li><p><strong>RouterOS</strong> is their operating system that runs on the hardware</p>
</li>
<li><p>It's like Linux, but specially made for networking</p>
</li>
<li><p>You can buy their hardware, OR run it on regular computers</p>
</li>
</ul>
<p>Think of it like this: RouterOS is the brain, and MikroTik hardware is the body.</p>
<h2 id="heading-my-first-connection-problem">My First Connection Problem</h2>
<p>I plugged in the router and tried to connect. Big mistake #1 - I assumed the login was blank username and blank password.</p>
<p><strong>ERROR</strong>: "Wrong credentials"</p>
<p>After some googling, I found out newer RouterOS versions need:</p>
<ul>
<li><p>Username: <code>admin</code></p>
</li>
<li><p>Password: (leave blank)</p>
</li>
</ul>
<p>Then it immediately asks you to create a new password. Smart security feature!</p>
<h2 id="heading-three-ways-to-control-your-router">Three Ways to Control Your Router</h2>
<p>MikroTik gives you three ways to control your router:</p>
<ol>
<li><p><strong>Winbox</strong> - A Windows program (easiest for beginners)</p>
</li>
<li><p><strong>WebFig</strong> - Use your web browser</p>
</li>
<li><p><strong>CLI/SSH</strong> - Command line (for advanced users)</p>
</li>
</ol>
<p>I started with Winbox because it looked the friendliest, but quickly discovered the command line was easier to understand.</p>
<h2 id="heading-the-interface-confusion">The Interface Confusion</h2>
<p>When I first looked at the interfaces, I was confused. The router showed 7 interfaces, but I could only see 4 physical ports:</p>
<ul>
<li><p><code>ether1-4</code>: The actual physical ports you can touch</p>
</li>
<li><p><code>wlan1</code>: The WiFi interface</p>
</li>
<li><p><code>bridgeLocal</code>: A virtual interface grouping ports together</p>
</li>
<li><p><code>pwr-line1</code>: Just another name for an Ethernet port</p>
</li>
</ul>
<p><strong>Key Learning</strong>: Not all interfaces are physical ports you can see and touch.</p>
<h2 id="heading-my-first-commands">My First Commands</h2>
<p>Here are the basic commands I learned on Day 1:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Check the router name</span>
/system identity <span class="hljs-built_in">print</span>

<span class="hljs-comment"># Change the router name</span>
/system identity <span class="hljs-built_in">set</span> name=<span class="hljs-string">"Alex-Lab1"</span>

<span class="hljs-comment"># See all network interfaces</span>
/interface <span class="hljs-built_in">print</span>

<span class="hljs-comment"># Make a backup of settings</span>
/system backup save name=factory-backup

<span class="hljs-comment"># Set the correct time</span>
/system clock <span class="hljs-built_in">print</span>
</code></pre>
<h2 id="heading-what-i-discovered-about-network-setup">What I Discovered About Network Setup</h2>
<p>The biggest surprise: Everything was connected to one big bridge called <code>bridgeLocal</code>. This meant:</p>
<ul>
<li><p>All 4 Ethernet ports acted like a switch</p>
</li>
<li><p>The WAN (internet) and LAN (local network) were mixed</p>
</li>
<li><p>My laptop couldn't get a proper IP address</p>
</li>
</ul>
<p>This explained why things weren't working as expected!</p>
<h2 id="heading-cli-vs-gui-my-preference">CLI vs GUI - My Preference</h2>
<p>I thought I'd use the GUI (graphical interface) for everything. But I quickly found the command line more logical:</p>
<ul>
<li><p>Commands follow a tree structure: <code>/system/identity</code>, <code>/interface</code></p>
</li>
<li><p>You can stay in sections and navigate around</p>
</li>
<li><p>Shortcuts work: <code>/int pr</code> instead of <code>/interface print</code></p>
</li>
<li><p>It just made more sense to me</p>
</li>
</ul>
<h2 id="heading-what-i-accomplished-on-day-1">What I Accomplished on Day 1</h2>
<p>By the end of Day 1, I had:</p>
<p>✅ Successfully connected to my MikroTik router<br />✅ Changed the system name from "MikroTik" to "Alex-Lab1"<br />✅ Created a backup of the original settings<br />✅ Set the system clock and timezone<br />✅ Mapped out all the physical and virtual interfaces<br />✅ Learned the basic navigation in both GUI and CLI</p>
<h2 id="heading-the-real-learning">The Real Learning</h2>
<p>The most important thing I learned wasn't technical - it was that <strong>MikroTik is different from consumer routers</strong>. It doesn't try to guess what you want. It gives you complete control, but you need to understand what you're doing.</p>
<p>This is both powerful and intimidating for a beginner.</p>
<h2 id="heading-whats-next">What's Next?</h2>
<p>Day 1 was just about getting familiar with the system. Tomorrow (Module 2), I need to:</p>
<ul>
<li><p>Fix the network setup so that WAN and LAN are properly separated</p>
</li>
<li><p>Configure IP addresses correctly</p>
</li>
<li><p>Get my laptop to connect to the internet through the router</p>
</li>
</ul>
<p>The journey has just begun, but I'm already seeing why network professionals love MikroTik - it's incredibly powerful once you understand the basics.</p>
<hr />
<p><em>This is part of my MikroTik Zero to Hero challenge. Follow along as I document my learning journey from complete beginner to confident network administrator.</em></p>
<p><strong>Next up</strong>: Module 2 - IP Addressing &amp; Basic Connectivity</p>
]]></content:encoded></item></channel></rss>