<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jeff Ronnie</title>
    <description>The latest articles on Forem by Jeff Ronnie (@jeff_ronnie_de6871efb7dcb).</description>
    <link>https://forem.com/jeff_ronnie_de6871efb7dcb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3817661%2Fc4545b2a-284a-43f2-9d33-5e8098a9f4e2.jpg</url>
      <title>Forem: Jeff Ronnie</title>
      <link>https://forem.com/jeff_ronnie_de6871efb7dcb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jeff_ronnie_de6871efb7dcb"/>
    <language>en</language>
    <item>
      <title>It's Never Too Late to Start</title>
      <dc:creator>Jeff Ronnie</dc:creator>
      <pubDate>Tue, 12 May 2026 12:03:26 +0000</pubDate>
      <link>https://forem.com/jeff_ronnie_de6871efb7dcb/its-never-too-late-to-start-3iin</link>
      <guid>https://forem.com/jeff_ronnie_de6871efb7dcb/its-never-too-late-to-start-3iin</guid>
      <description>&lt;p&gt;There’s a common myth that if you didn’t start coding at age twelve, you’ve already missed the boat. I’m here to tell you, from the heart of Kisumu, that is absolutely false. My journey didn't start with a keyboard in my hand; it started with a conviction that the gap between understanding a network and securing one was a gap worth closing.&lt;/p&gt;

&lt;p&gt;The Pivot: From Theory to Tooling&lt;/p&gt;

&lt;p&gt;I spent four years at the Technical University of Kenya studying Communication and Computer Networks. I could draw you a packet header from memory, but I couldn't write the code to catch one. The shift happened when I stopped being a student of "theory" and became a student of "production."&lt;/p&gt;

&lt;p&gt;If you are a beginner sitting with a Go tutorial open or any other programming language, feeling overwhelmed: Keep going. A month ago, I was just learning the syntax of golang. Today, I am building SME-Shield, a full-stack security dashboard designed to protect small businesses in Kenya.&lt;/p&gt;

&lt;p&gt;What Scaling a Project Proves&lt;/p&gt;

&lt;p&gt;Transitioning from a CLI (Command Line Interface) tool to a Full-stack product isn’t just about adding a "pretty face." It’s a complete mental overhaul. Here is what that process proves to you as a developer:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Logic is Universal: Whether it's a worker pool in Go or a state-handler in JavaScript, the problem-solving logic remains the same.

Data has a Life Cycle: In a CLI, data is transient. In a Full-stack app, you have to care about its "home" (SQLite), its "travel" (REST APIs), and its "presentation" (Tailwind CSS).

The "User" is Your True North: Building for a terminal is building for yourself. Building a dashboard is building for an SME owner who needs to see a "Security Score" to feel safe.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Zone01 Kisumu Factor&lt;/p&gt;

&lt;p&gt;I wouldn’t be writing this without the environment at Zone01 Kisumu. Tech isn't a solo sport. It’s about being in a room where someone is specialized in Cloud, another in AI, and another in Blockchain and realize you can learn from all of them. This collaborative, project-based model forced me to build something real, and that is the fastest way to bridge the gap.&lt;/p&gt;

&lt;p&gt;My Experience: A Note to the Beginners&lt;/p&gt;

&lt;p&gt;If you are worried that you’re "just a beginner," remember that every professional-grade product started as a buggy script. My scanner started as a simple ping; today it’s a vulnerability auditor that cross-references the NVD database.&lt;/p&gt;

&lt;p&gt;The takeaway? Don't wait until you "know enough" to start a project. Start the project so that you are forced to learn what you don't know.  &lt;/p&gt;

</description>
      <category>devops</category>
      <category>beginners</category>
      <category>career</category>
      <category>go</category>
    </item>
    <item>
      <title>Why Project-Based Learning Works: Building My First Port Scanner in Go</title>
      <dc:creator>Jeff Ronnie</dc:creator>
      <pubDate>Wed, 25 Mar 2026 12:42:45 +0000</pubDate>
      <link>https://forem.com/jeff_ronnie_de6871efb7dcb/why-project-based-learning-works-building-my-first-port-scanner-in-go-1n6k</link>
      <guid>https://forem.com/jeff_ronnie_de6871efb7dcb/why-project-based-learning-works-building-my-first-port-scanner-in-go-1n6k</guid>
      <description>&lt;p&gt;Introduction&lt;/p&gt;

&lt;p&gt;I spent four years studying Communication and Computer Networks at the Technical University of Kenya. I was fairly solid on theory and could explain TCP/IP handshakes, draw network topologies, and describe exactly what happens when a packet moves from one host to another.&lt;/p&gt;

&lt;p&gt;Then I joined Zone01 Kisumu, an environment that forced me to build something with the knowledge I had. That’s when I realized there’s a massive gap between knowing how networking works and knowing how to code it.&lt;/p&gt;

&lt;p&gt;This is the story of how I bridged that gap. Let me walk you through the journey; from building a concurrent port scanner in Go to developing a strong conviction that real projects are the fastest way to learn.&lt;/p&gt;




&lt;p&gt;To understand how this project pushed me, let’s start by explaining the technical challenge at its core: what exactly is a port scanner?&lt;/p&gt;

&lt;p&gt;A port scanner probes a target IP address and checks which of the listed ports are open, closed, or filtered. Tools like nmap do this at a professional level. My scanner is a simpler version, and building it taught me more than any lecture ever did.&lt;/p&gt;




&lt;p&gt;The Stack&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Language: Go&lt;/li&gt;
&lt;li&gt;Concurrency model: Worker pools with goroutines&lt;/li&gt;
&lt;li&gt;Input: CLI flags (target IP/range, port range, workers, timeout)&lt;/li&gt;
&lt;li&gt;Output: Terminal display + optional file export (JSON, CSV, TXT)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;How It Works&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parsing the Target&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The scanner accepts a single IP address or a range, such as 192.168.1.1-192.168.1.254. The ParseIPs() function in the scanner package handles expanding that range into a slice of individual IP strings.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parsing Ports&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was one of the more interesting parts to design with flexibility in mind. I wanted to be able to scan a range (1-1000), a comma-separated list (22,80,443), or use a named profile:&lt;/p&gt;

&lt;p&gt;func parsePorts(input string) ([]int, error) {&lt;br&gt;
    // check for named profile first&lt;br&gt;
    if profile, ok := scanner.PortProfiles[input]; ok {&lt;br&gt;
        return profile, nil&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var ports []int

if strings.Contains(input, "-") {
    parts := strings.Split(input, "-")
    start, err1 := strconv.Atoi(parts[0])
    end, err2 := strconv.Atoi(parts[1])
    if err1 != nil || err2 != nil {
        return nil, fmt.Errorf("invalid port numbers in range: %s", input)
    }
    for i := start; i &amp;lt;= end; i++ {
        ports = append(ports, i)
    }
    return ports, nil
}

for _, p := range strings.Split(input, ",") {
    port, err := strconv.Atoi(strings.TrimSpace(p))
    if err != nil || port &amp;lt; 1 || port &amp;gt; 65535 {
        return nil, fmt.Errorf("invalid port: %s", p)
    }
    ports = append(ports, port)
}
return ports, nil
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;Port profiles like common, web, db, and ssh are predefined slices in the scanner package. So instead of typing --ports 22,80,443,3306,5432, you just type --ports web. Much cleaner.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Worker Pool (Where Go Shines)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is my proudest bit. Scanning hundreds of IPs across hundreds of ports sequentially would take forever. The solution? Concurrency.&lt;/p&gt;

&lt;p&gt;Go makes this elegant with go routines. These are lightweight threads that can run thousands at a time. My scanner uses a worker pool pattern: a fixed number of workers pick jobs from a shared channel and process them concurrently.&lt;/p&gt;

&lt;p&gt;results := scanner.RunWorkerPool(ips, portList, *workers, time.Duration(*timeout)*time.Second)&lt;/p&gt;

&lt;p&gt;By default, 100 workers run simultaneously. That means scanning 254 hosts across 100 ports completes in seconds, not minutes.&lt;/p&gt;

&lt;p&gt;This was the hardest concept to get right. You’re not writing step-by-step instructions anymore; you’re designing a system where many things happen at once.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The CLI Interface&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main.go uses Go’s built-in flag package to handle all inputs cleanly:&lt;/p&gt;

&lt;p&gt;target   := flag.String("target", "", "IP or range (e.g. 192.168.89.1-192.168.89.254)")&lt;br&gt;
ports    := flag.String("ports", "common", "Ports: range, list, or profile (common, web, db, ssh)")&lt;br&gt;
workers  := flag.Int("workers", 100, "Number of concurrent workers")&lt;br&gt;
timeout  := flag.Int("timeout", 1, "Connection timeout in seconds")&lt;br&gt;
verbose  := flag.Bool("v", false, "Show closed ports too")&lt;br&gt;
vverbose := flag.Bool("vv", false, "Show all ports including filtered")&lt;br&gt;
output   := flag.String("output", "", "Save results to file (e.g. results.json, results.csv, results.txt)")&lt;/p&gt;

&lt;p&gt;A typical scan looks like this:&lt;/p&gt;

&lt;p&gt;go run main.go -target 192.168.1.1-192.168.1.50 -ports web -workers 200 -output results.json&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Output &amp;amp; Saving Results&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The scanner prints results to the terminal and can optionally save them to a file. I added support for three formats; JSON, CSV, and plain text, because different use cases need different outputs. A security audit might want JSON for parsing; a quick report might want CSV for a spreadsheet.&lt;/p&gt;




&lt;p&gt;What I Learned That School Didn’t Teach Me&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Networking concepts look different in code.&lt;br&gt;
I knew what a TCP connection was. But writing code that attempts a TCP dial to check if a port is open, and then handling timeouts, refused connections, and filtered ports as separate outcomes, gave me a much deeper intuition for what’s actually happening on the wire.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Concurrency is a design problem, not just a syntax problem.&lt;br&gt;
Go’s go routines are simple to write. Worker pools are simple to understand. But designing a concurrent system, deciding how many workers, how to handle shared state, how to collect results safely   requires a different way of thinking that you only develop by doing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CLI design matters.&lt;br&gt;
A tool that’s hard to use won’t get used. Adding port profiles, verbosity levels, and multiple output formats wasn’t about making the code fancier, it was about making the tool actually useful in real scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Error handling is where discipline lives.&lt;br&gt;
Go forces you to handle errors explicitly. At first, that felt annoying. Now I see it as one of the language’s best features. Every if err != nil block is a moment where you decide: what should actually happen here?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;What’s Next&lt;/p&gt;

&lt;p&gt;This scanner works well for small networks, which is exactly the audience I had in mind, SMEs in Kenya who need a lightweight, local tool for basic network visibility without the overhead of enterprise software.&lt;/p&gt;

&lt;p&gt;Next steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add OS fingerprinting&lt;/li&gt;
&lt;li&gt;Build a simple web UI for non-technical users.&lt;/li&gt;
&lt;li&gt;Package it as a standalone binary for easy distribution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code is open source on GitHub: github.com/rjeff-sudo&lt;/p&gt;




&lt;p&gt;Final Thought&lt;/p&gt;

&lt;p&gt;If you’re learning a new language or trying to solidify concepts from school, stop doing tutorials in isolation. Pick something real, something slightly too hard for your current level, and build it. You’ll hit walls. You’ll Google things at 1am. You’ll rewrite entire functions because you didn’t think it through the first time.&lt;/p&gt;

&lt;p&gt;That’s not failure. That’s learning.&lt;/p&gt;




&lt;p&gt;Built with Go · Zone01 Kisumu · Nairobi, Kenya&lt;/p&gt;

&lt;p&gt;If you found this useful or have questions about the code, drop a comment below. I’m always happy to talk about networking and Go.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>go</category>
      <category>networking</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
