<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kevin Nambubbi</title>
    <description>The latest articles on DEV Community by Kevin Nambubbi (@kev_luciano).</description>
    <link>https://dev.to/kev_luciano</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%2F3732443%2Fdb241c91-d685-4a5f-881f-b179e171782e.png</url>
      <title>DEV Community: Kevin Nambubbi</title>
      <link>https://dev.to/kev_luciano</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kev_luciano"/>
    <language>en</language>
    <item>
      <title>Why Developers Shouldn't Blindly Trust AI-Generated Code: Lessons From a Real Project</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Tue, 09 Jun 2026 01:06:42 +0000</pubDate>
      <link>https://dev.to/kev_luciano/why-developers-shouldnt-blindly-trust-ai-generated-code-lessons-from-a-real-project-31nb</link>
      <guid>https://dev.to/kev_luciano/why-developers-shouldnt-blindly-trust-ai-generated-code-lessons-from-a-real-project-31nb</guid>
      <description>&lt;p&gt;Artificial intelligence has become a permanent part of modern software development.&lt;/p&gt;

&lt;p&gt;Tools like ChatGPT, GitHub Copilot, Claude, Cursor, and others can generate code in seconds that would otherwise take minutes or hours to write manually.&lt;/p&gt;

&lt;p&gt;But after recently completing a JavaScript project, I learned an important lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI-generated code is not the same thing as production-ready code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Project
&lt;/h2&gt;

&lt;p&gt;The assignment involved building a superhero directory application using data from a public API.&lt;/p&gt;

&lt;p&gt;The application required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data fetching&lt;/li&gt;
&lt;li&gt;Table rendering&lt;/li&gt;
&lt;li&gt;Pagination&lt;/li&gt;
&lt;li&gt;Live search&lt;/li&gt;
&lt;li&gt;Column sorting&lt;/li&gt;
&lt;li&gt;Modal detail views&lt;/li&gt;
&lt;li&gt;URL state persistence&lt;/li&gt;
&lt;li&gt;Performance considerations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first glance, this seems like the perfect use case for AI assistance.&lt;/p&gt;

&lt;p&gt;So I started relying heavily on AI-generated solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Trap
&lt;/h2&gt;

&lt;p&gt;Every time I encountered a bug, I requested a complete replacement for the affected code.&lt;/p&gt;

&lt;p&gt;Examples included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pagination issues&lt;/li&gt;
&lt;li&gt;Search behavior&lt;/li&gt;
&lt;li&gt;Sorting problems&lt;/li&gt;
&lt;li&gt;Modal bugs&lt;/li&gt;
&lt;li&gt;Missing table data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI frequently provided code that looked correct.&lt;/p&gt;

&lt;p&gt;Sometimes it even sounded confident.&lt;/p&gt;

&lt;p&gt;Unfortunately, confidence is not correctness.&lt;/p&gt;

&lt;p&gt;Several generated solutions introduced new problems while attempting to solve existing ones.&lt;/p&gt;

&lt;p&gt;Examples included:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. HTML and JavaScript Mismatches
&lt;/h3&gt;

&lt;p&gt;The table rendering function produced fifteen columns of data while the HTML contained only eight table headers.&lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Misaligned data&lt;/li&gt;
&lt;li&gt;Broken sorting&lt;/li&gt;
&lt;li&gt;Confusing UI behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code technically executed, but the application was incorrect.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Incorrect Numerical Sorting
&lt;/h3&gt;

&lt;p&gt;Strings like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;78 kg&lt;/li&gt;
&lt;li&gt;100 kg&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;were being compared alphabetically rather than numerically.&lt;/p&gt;

&lt;p&gt;This caused:&lt;/p&gt;

&lt;p&gt;100 kg&lt;/p&gt;

&lt;p&gt;to appear before&lt;/p&gt;

&lt;p&gt;78 kg&lt;/p&gt;

&lt;p&gt;because string comparison evaluates "1" before "7".&lt;/p&gt;

&lt;p&gt;The correct solution required extracting numeric values before sorting.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Regressions
&lt;/h3&gt;

&lt;p&gt;A common pattern emerged:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bug identified.&lt;/li&gt;
&lt;li&gt;AI generated fix.&lt;/li&gt;
&lt;li&gt;Original bug disappeared.&lt;/li&gt;
&lt;li&gt;Two new bugs appeared.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This created a cycle where progress felt constant but actual completion remained distant.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Finally Worked
&lt;/h2&gt;

&lt;p&gt;Instead of asking for another complete rewrite, I began auditing the application manually.&lt;/p&gt;

&lt;p&gt;I checked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DOM structure&lt;/li&gt;
&lt;li&gt;Event listeners&lt;/li&gt;
&lt;li&gt;Render functions&lt;/li&gt;
&lt;li&gt;Data mappings&lt;/li&gt;
&lt;li&gt;Sort implementations&lt;/li&gt;
&lt;li&gt;API response structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Within a short period, I found several root causes that had been hidden beneath layers of generated code.&lt;/p&gt;

&lt;p&gt;The lesson was simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding the system is faster than repeatedly replacing the system.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where AI Excels
&lt;/h2&gt;

&lt;p&gt;Despite the challenges, AI was still useful.&lt;/p&gt;

&lt;p&gt;It helped with:&lt;/p&gt;

&lt;h3&gt;
  
  
  Boilerplate
&lt;/h3&gt;

&lt;p&gt;Generating repetitive code quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Explaining APIs and concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brainstorming
&lt;/h3&gt;

&lt;p&gt;Suggesting approaches I might not have considered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning
&lt;/h3&gt;

&lt;p&gt;Helping understand unfamiliar patterns.&lt;/p&gt;

&lt;p&gt;Used correctly, AI significantly improved productivity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where AI Should Not Be Trusted Blindly
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Production Logic
&lt;/h3&gt;

&lt;p&gt;Always verify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Decisions
&lt;/h3&gt;

&lt;p&gt;Always review.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security-Critical Code
&lt;/h3&gt;

&lt;p&gt;Always inspect manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Optimization
&lt;/h3&gt;

&lt;p&gt;Always benchmark.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug Fixes
&lt;/h3&gt;

&lt;p&gt;Always reproduce and validate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Advice
&lt;/h2&gt;

&lt;p&gt;If you use AI in development:&lt;/p&gt;

&lt;h3&gt;
  
  
  Treat AI like a junior developer
&lt;/h3&gt;

&lt;p&gt;Review everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read generated code line by line
&lt;/h3&gt;

&lt;p&gt;If you cannot explain it, do not ship it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep requirements visible
&lt;/h3&gt;

&lt;p&gt;Many AI mistakes come from drifting away from the original specification.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test continuously
&lt;/h3&gt;

&lt;p&gt;Never assume generated code works because it looks reasonable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debug before regenerating
&lt;/h3&gt;

&lt;p&gt;Understanding the bug is often faster than generating another solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;AI is transforming software development.&lt;/p&gt;

&lt;p&gt;But there is a difference between:&lt;/p&gt;

&lt;p&gt;"AI helped me write code"&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;"AI built my application."&lt;/p&gt;

&lt;p&gt;One is a productivity multiplier.&lt;/p&gt;

&lt;p&gt;The other is a gamble.&lt;/p&gt;

&lt;p&gt;The strongest developers will not be those who reject AI.&lt;/p&gt;

&lt;p&gt;They will be those who understand when to trust it, when to challenge it, and when to ignore it completely.&lt;/p&gt;

&lt;p&gt;AI can write code.&lt;/p&gt;

&lt;p&gt;Engineers are still responsible for making sure that code is correct.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>From Syntax to Systems</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Thu, 04 Jun 2026 12:07:48 +0000</pubDate>
      <link>https://dev.to/kev_luciano/from-syntax-to-systems-3a51</link>
      <guid>https://dev.to/kev_luciano/from-syntax-to-systems-3a51</guid>
      <description>&lt;h1&gt;
  
  
  From Learning Syntax to Building Systems
&lt;/h1&gt;

&lt;p&gt;When I started programming, I believed that mastering syntax was the main challenge.&lt;/p&gt;

&lt;p&gt;If I could understand variables, loops, functions, structs, and conditionals, then surely I'd be ready to build software.&lt;/p&gt;

&lt;p&gt;Recently, while working on a social network project, I discovered something important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowing syntax and building systems are two completely different skills.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Alphabet Analogy
&lt;/h2&gt;

&lt;p&gt;Imagine a child who can confidently recite:&lt;/p&gt;

&lt;p&gt;A, B, C, D, E, F, G...&lt;/p&gt;

&lt;p&gt;That's impressive.&lt;/p&gt;

&lt;p&gt;The child knows the alphabet.&lt;/p&gt;

&lt;p&gt;But now ask the same child to write a report, tell a story, or explain a scientific concept.&lt;/p&gt;

&lt;p&gt;Suddenly, knowing the alphabet is no longer enough.&lt;/p&gt;

&lt;p&gt;The challenge has shifted from recognizing letters to organizing ideas.&lt;/p&gt;

&lt;p&gt;Programming feels the same way.&lt;/p&gt;

&lt;p&gt;I know Go syntax.&lt;/p&gt;

&lt;p&gt;I can define structs.&lt;/p&gt;

&lt;p&gt;I can write functions.&lt;/p&gt;

&lt;p&gt;I can use loops and conditionals.&lt;/p&gt;

&lt;p&gt;But when I was asked to help build a social network, I found myself facing questions that syntax couldn't answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers Analogy
&lt;/h2&gt;

&lt;p&gt;Another comparison helped me understand the problem.&lt;/p&gt;

&lt;p&gt;Imagine someone who knows numbers:&lt;/p&gt;

&lt;p&gt;1, 2, 3, 4, 5...&lt;/p&gt;

&lt;p&gt;They can recognize them perfectly.&lt;/p&gt;

&lt;p&gt;But that doesn't automatically mean they can solve algebra, calculus, or complex mathematical problems.&lt;/p&gt;

&lt;p&gt;The symbols are only tools.&lt;/p&gt;

&lt;p&gt;The real challenge is knowing how to use them.&lt;/p&gt;

&lt;p&gt;Programming syntax works the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Building Systems Actually Looks Like
&lt;/h2&gt;

&lt;p&gt;For example, I was assigned responsibility for features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Followers&lt;/li&gt;
&lt;li&gt;Posts&lt;/li&gt;
&lt;li&gt;Comments&lt;/li&gt;
&lt;li&gt;Feed&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first, I thought:&lt;/p&gt;

&lt;p&gt;"Which Go syntax should I use?"&lt;/p&gt;

&lt;p&gt;But experienced developers approach the problem differently.&lt;/p&gt;

&lt;p&gt;They ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What data exists?&lt;/li&gt;
&lt;li&gt;What relationships exist?&lt;/li&gt;
&lt;li&gt;What questions must the system answer?&lt;/li&gt;
&lt;li&gt;What business rules must be enforced?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a simple follow request, the thought process becomes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does the sender exist?&lt;/li&gt;
&lt;li&gt;Does the receiver exist?&lt;/li&gt;
&lt;li&gt;Is the sender trying to follow themselves?&lt;/li&gt;
&lt;li&gt;Is there already a pending request?&lt;/li&gt;
&lt;li&gt;Are they already following the user?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only after answering those questions does the code become relevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Biggest Lesson So Far
&lt;/h2&gt;

&lt;p&gt;The biggest lesson I've learned is that software engineering is not primarily about code.&lt;/p&gt;

&lt;p&gt;It's about modeling real-world behavior.&lt;/p&gt;

&lt;p&gt;The code is simply the implementation of that model.&lt;/p&gt;

&lt;p&gt;Today I'm learning to move from:&lt;/p&gt;

&lt;p&gt;"I know the syntax."&lt;/p&gt;

&lt;p&gt;to&lt;/p&gt;

&lt;p&gt;"I understand the system."&lt;/p&gt;

&lt;p&gt;And I think that transition is where real software engineering begins.&lt;/p&gt;

</description>
      <category>go</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>How Zone01 Kisumu "Build from Scratch" Approach Transformed Me from a Framework User to a Problem Solver</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Sun, 31 May 2026 06:24:40 +0000</pubDate>
      <link>https://dev.to/kev_luciano/how-zone01-kisumu-build-from-scratch-approach-transformed-me-from-a-framework-user-to-a-problem-4gb1</link>
      <guid>https://dev.to/kev_luciano/how-zone01-kisumu-build-from-scratch-approach-transformed-me-from-a-framework-user-to-a-problem-4gb1</guid>
      <description>&lt;p&gt;The Moment I Realized I Didn't Really Know JavaScript&lt;br&gt;
I was 2 months into learning JavaScript. I could use .map(), .filter(), .reduce() like any bootcamp grad. I felt confident.&lt;/p&gt;

&lt;p&gt;Then my instructor asked me one question:&lt;/p&gt;

&lt;p&gt;"How does .reverse() actually work?"&lt;/p&gt;

&lt;p&gt;I froze.&lt;/p&gt;

&lt;p&gt;I had used it hundreds of times. But I had no idea what was happening inside. I was a user, not a builder.&lt;/p&gt;

&lt;p&gt;That was the day everything changed.&lt;/p&gt;

&lt;p&gt;The 01EDU Difference: Build Tools, Not Just Use Them&lt;br&gt;
Most coding courses teach you to use built-in methods. 01EDU does something different.&lt;/p&gt;

&lt;p&gt;They disable the built-in methods.&lt;/p&gt;

&lt;p&gt;Then they say: "Now build it yourself."&lt;/p&gt;

&lt;p&gt;No .split(). No .join(). No .indexOf(). No .slice().&lt;/p&gt;

&lt;p&gt;Just you, a text editor, and your brain.&lt;/p&gt;

&lt;p&gt;What I Built in 2 Weeks (Without Using Built-ins)&lt;br&gt;
Here are the JavaScript methods I re-created from scratch:&lt;/p&gt;

&lt;p&gt;Method  What I Learned&lt;br&gt;
abs()   Math is logic, not magic&lt;br&gt;
multiply(), divide(), modulo()  Arithmetic is repeated addition/subtraction&lt;br&gt;
indexOf(), lastIndexOf(), includes()    Searching is just looping and comparing&lt;br&gt;
slice() Negative indexes count from the end&lt;br&gt;
reverse()   Arrays and strings are both indexed collections&lt;br&gt;
join()  Building strings step by step&lt;br&gt;
split() Parsing is character-by-character inspection&lt;br&gt;
round(), floor(), ceil(), trunc()   Decimals are just numbers between whole numbers&lt;br&gt;
Each function took hours of thinking, failing, debugging, and finally — understanding.&lt;/p&gt;

&lt;p&gt;The Most Painful Lesson: Loops&lt;br&gt;
The first time I tried to build repeat() without using .repeat(), I wrote an infinite loop.&lt;/p&gt;

&lt;p&gt;My computer froze. I had to force restart.&lt;/p&gt;

&lt;p&gt;That failure taught me more than any working code ever could. I learned to trace each iteration mentally. I learned to check my exit conditions. I learned to respect the loop.&lt;/p&gt;

&lt;p&gt;You don't truly understand loops until you've crashed your computer with one.&lt;/p&gt;

&lt;p&gt;What 01EDU Taught Me That No Bootcamp Could&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I learned how computers think, not just how to write code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you build .split() from scratch, you understand string parsing at a deep level. You know that every character is checked, one by one. You know that separators are just conditional triggers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I stopped fearing errors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Error messages became my teachers. Each "undefined" or "not a function" showed me exactly where my mental model was wrong. I learned to read the stack trace before asking for help.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I can now learn any language faster&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I look at Python or Go, I don't see strange syntax. I see loops, conditions, and variables — the same building blocks I already mastered.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I interview differently&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When an interviewer asks "How does array.map() work?", I don't just describe its behavior. I explain the loop, the callback, the new array creation, and the return. I've actually built it.&lt;/p&gt;

&lt;p&gt;The Hard Truth: Shortcuts Create Ceilings&lt;br&gt;
Using built-in methods is like using a calculator. It's fast. It's efficient. But if you never learn long division, you'll never truly understand numbers.&lt;/p&gt;

&lt;p&gt;The same applies to code.&lt;/p&gt;

&lt;p&gt;If you only use .sort(), you won't understand sorting algorithms&lt;/p&gt;

&lt;p&gt;If you only use .split(), you won't understand string parsing&lt;/p&gt;

&lt;p&gt;If you only use Math.round(), you won't understand floating-point math&lt;/p&gt;

&lt;p&gt;Built-in methods are not the enemy. But they should be earned, not given.&lt;/p&gt;

&lt;p&gt;How You Can Do This Too (Without Enrolling in 01EDU)&lt;br&gt;
You don't need a special school to learn this way.&lt;/p&gt;

&lt;p&gt;Try this challenge:&lt;/p&gt;

&lt;p&gt;For one week, pretend these methods don't exist:&lt;/p&gt;

&lt;p&gt;.map(), .filter(), .reduce()&lt;/p&gt;

&lt;p&gt;.split(), .join()&lt;/p&gt;

&lt;p&gt;.indexOf(), .includes()&lt;/p&gt;

&lt;p&gt;Math.round(), Math.floor(), Math.ceil()&lt;/p&gt;

&lt;p&gt;.slice(), .reverse()&lt;/p&gt;

&lt;p&gt;Build your own versions. Use only loops, conditionals, and basic math.&lt;/p&gt;

&lt;p&gt;You will struggle. You will feel slow. But after one week, you will understand JavaScript better than most developers with years of experience.&lt;/p&gt;

&lt;p&gt;What's Next for Me&lt;br&gt;
I am now building:&lt;/p&gt;

&lt;p&gt;My own map() and filter()&lt;/p&gt;

&lt;p&gt;A deep understanding of recursion by building tree traversal&lt;/p&gt;

&lt;p&gt;A basic promise implementation to understand async JavaScript&lt;/p&gt;

&lt;p&gt;Each one is harder than the last. But that's the point.&lt;/p&gt;

&lt;p&gt;The goal is not to finish. The goal is to understand.&lt;/p&gt;

</description>
      <category>01edu</category>
      <category>zone01</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Why I’m Learning Go and JavaScript Together Instead of Separately</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Fri, 29 May 2026 20:10:27 +0000</pubDate>
      <link>https://dev.to/kev_luciano/why-im-learning-go-and-javascript-together-4ih</link>
      <guid>https://dev.to/kev_luciano/why-im-learning-go-and-javascript-together-4ih</guid>
      <description>&lt;p&gt;Most programming tutorials teach technologies in isolation.&lt;/p&gt;

&lt;p&gt;You learn a language.&lt;br&gt;
Then a framework.&lt;br&gt;
Then a database.&lt;br&gt;
Then eventually build a small CRUD project.&lt;/p&gt;

&lt;p&gt;But real systems are not built in isolation.&lt;/p&gt;

&lt;p&gt;Modern applications are combinations of specialized services, each optimized for a particular responsibility.&lt;/p&gt;

&lt;p&gt;That realization changed how I approached learning software engineering.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Background
&lt;/h2&gt;

&lt;p&gt;I recently completed an intensive introduction to Go and I’m currently transitioning into JavaScript and Node.js.&lt;/p&gt;

&lt;p&gt;One thing became obvious almost immediately:&lt;/p&gt;

&lt;p&gt;JavaScript and Go overlap in interesting ways on the backend.&lt;/p&gt;

&lt;p&gt;At first, this confused me.&lt;/p&gt;

&lt;p&gt;Why would a system use both?&lt;br&gt;
Why not choose one language and use it everywhere?&lt;/p&gt;

&lt;p&gt;The deeper I looked into production systems, the more the answer made sense.&lt;/p&gt;
&lt;h2&gt;
  
  
  Different Strengths, Different Responsibilities
&lt;/h2&gt;

&lt;p&gt;Go excels at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concurrency&lt;/li&gt;
&lt;li&gt;resource efficiency&lt;/li&gt;
&lt;li&gt;scalable backend services&lt;/li&gt;
&lt;li&gt;systems programming&lt;/li&gt;
&lt;li&gt;infrastructure tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JavaScript (through Node.js) excels at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rapid API development&lt;/li&gt;
&lt;li&gt;realtime systems&lt;/li&gt;
&lt;li&gt;event-driven architecture&lt;/li&gt;
&lt;li&gt;frontend/backend integration&lt;/li&gt;
&lt;li&gt;ecosystem flexibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These strengths are complementary rather than competitive.&lt;/p&gt;

&lt;p&gt;A realistic production architecture might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Frontend (React)
        ↓
Node.js API Gateway
        ↓
Go Processing Service
        ↓
PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Node.js layer handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;REST APIs&lt;/li&gt;
&lt;li&gt;realtime websocket communication&lt;/li&gt;
&lt;li&gt;frontend coordination&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Go service handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concurrent processing&lt;/li&gt;
&lt;li&gt;analytics&lt;/li&gt;
&lt;li&gt;alert generation&lt;/li&gt;
&lt;li&gt;heavy background workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation reflects how many modern distributed systems are designed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Something Production-Minded
&lt;/h2&gt;

&lt;p&gt;Instead of building tutorial projects, I decided to start building a system called “Sentra.”&lt;/p&gt;

&lt;p&gt;Sentra is an incident reporting and monitoring platform designed to simulate real operational systems.&lt;/p&gt;

&lt;p&gt;The platform will include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;incident management&lt;/li&gt;
&lt;li&gt;realtime alerts&lt;/li&gt;
&lt;li&gt;concurrent event processing&lt;/li&gt;
&lt;li&gt;audit logging&lt;/li&gt;
&lt;li&gt;service-to-service communication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective is not simply to “finish an app.”&lt;/p&gt;

&lt;p&gt;The objective is to learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;architectural boundaries&lt;/li&gt;
&lt;li&gt;production thinking&lt;/li&gt;
&lt;li&gt;operational reliability&lt;/li&gt;
&lt;li&gt;security-first engineering&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Security From Day One
&lt;/h2&gt;

&lt;p&gt;One principle I strongly believe in:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Security is not a feature but the fundamental of systems.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many beginner tutorials postpone security until later.&lt;br&gt;
That creates dangerous engineering habits.&lt;/p&gt;

&lt;p&gt;Even at the earliest stages, systems should account for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;input validation&lt;/li&gt;
&lt;li&gt;password hashing&lt;/li&gt;
&lt;li&gt;environment variable protection&lt;/li&gt;
&lt;li&gt;rate limiting&lt;/li&gt;
&lt;li&gt;secure headers&lt;/li&gt;
&lt;li&gt;least privilege principles&lt;/li&gt;
&lt;li&gt;proper logging practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A system that works but is insecure is incomplete.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’m Focusing On
&lt;/h2&gt;

&lt;p&gt;As I continue building Sentra, I’ll be focusing heavily on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;backend architecture&lt;/li&gt;
&lt;li&gt;Go concurrency patterns&lt;/li&gt;
&lt;li&gt;Node.js middleware design&lt;/li&gt;
&lt;li&gt;PostgreSQL data modeling&lt;/li&gt;
&lt;li&gt;Dockerized environments&lt;/li&gt;
&lt;li&gt;observability and monitoring&lt;/li&gt;
&lt;li&gt;clean repository practices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m less interested in “toy apps” and more interested in understanding how production systems evolve from simple foundations into scalable services.&lt;/p&gt;

&lt;p&gt;The goal is to think like a systems engineer early, even while still learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Learning syntax is important.&lt;/p&gt;

&lt;p&gt;But learning how systems communicate, fail, recover, scale, and remain secure is where software engineering becomes truly interesting.&lt;/p&gt;

&lt;p&gt;That’s the direction I’m pursuing now.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>go</category>
      <category>javascript</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Golang Trinity: Functions, Methods, Interfaces</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Thu, 28 May 2026 01:32:09 +0000</pubDate>
      <link>https://dev.to/kev_luciano/the-golang-trinity-functions-methods-interfaces-31pa</link>
      <guid>https://dev.to/kev_luciano/the-golang-trinity-functions-methods-interfaces-31pa</guid>
      <description>&lt;p&gt;&lt;strong&gt;Function&lt;/strong&gt;: Does something with inputs&lt;br&gt;
&lt;strong&gt;Method&lt;/strong&gt;  Does something attached to a type&lt;br&gt;
&lt;strong&gt;Interface&lt;/strong&gt;   Says what methods a type must have&lt;/p&gt;

&lt;p&gt;A method is a function with a receiver.&lt;br&gt;
An interface is a set of method signatures.&lt;br&gt;
If a type has those methods → it satisfies the interface. Automatically.&lt;/p&gt;

&lt;p&gt;No implements. No inheritance. Just behavior.&lt;/p&gt;

&lt;p&gt;// Function&lt;br&gt;
func Add(a, b int) int { return a + b }&lt;/p&gt;

&lt;p&gt;type Counter struct{ val int }&lt;/p&gt;

&lt;p&gt;// Method (receiver is the "attachment")&lt;br&gt;
func (c Counter) Add(n int) int { return c.val + n }&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Function vs. Method
// Function
func Add(a, b int) int { return a + b }&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;type Counter struct{ val int }&lt;/p&gt;

&lt;p&gt;// Method (receiver is the "attachment")&lt;br&gt;
func (c Counter) Add(n int) int { return c.val + n }&lt;br&gt;
Same logic. Different style.&lt;br&gt;
Use functions for stateless operations.&lt;br&gt;
Use methods when behavior belongs to a type.&lt;/p&gt;

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

&lt;p&gt;type Stringer interface {&lt;br&gt;
    String() string&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;type User struct{ Name string }&lt;/p&gt;

&lt;p&gt;// User now implements Stringer automatically&lt;br&gt;
func (u User) String() string { return u.Name }&lt;/p&gt;

&lt;p&gt;func Print(s Stringer) { fmt.Println(s.String()) }&lt;/p&gt;

&lt;p&gt;func main() {&lt;br&gt;
    Print(User{Name: "Alice"}) // Works!&lt;br&gt;
}&lt;br&gt;
Key: The function Print doesn't care about User. It cares about the String() method.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Twist: Functions Can Be Interfaces
This is the pattern that confuses people.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// An interface&lt;br&gt;
type Handler interface {&lt;br&gt;
    Handle(string)&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// A function type&lt;br&gt;
type HandlerFunc func(string)&lt;/p&gt;

&lt;p&gt;// The trick: attach the Handle method to the function type&lt;br&gt;
func (f HandlerFunc) Handle(s string) {&lt;br&gt;
    f(s) // calls itself&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// Now any function with signature func(string) works as a Handler&lt;br&gt;
func Log(s string) { fmt.Println("LOG:", s) }&lt;/p&gt;

&lt;p&gt;func main() {&lt;br&gt;
    var h Handler = HandlerFunc(Log)&lt;br&gt;
    h.Handle("test") // LOG: test&lt;br&gt;
}&lt;br&gt;
Why is this useful?&lt;br&gt;
It lets you use simple functions where interfaces are expected.&lt;br&gt;
The standard http.HandlerFunc works exactly like this.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Putting It All Together
go
type Greeter interface { Greet() string }&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;type Person struct{ Name string }&lt;br&gt;
func (p Person) Greet() string { return "Hi, " + p.Name }&lt;/p&gt;

&lt;p&gt;type GreetFunc func() string&lt;br&gt;
func (gf GreetFunc) Greet() string { return gf() }&lt;/p&gt;

&lt;p&gt;func Party(g Greeter) { fmt.Println(g.Greet()) }&lt;/p&gt;

&lt;p&gt;func main() {&lt;br&gt;
    Party(Person{Name: "Bob"})                    // Method&lt;br&gt;
    Party(GreetFunc(func() string {               // Function as interface&lt;br&gt;
        return "Surprise!"&lt;br&gt;
    }))&lt;br&gt;
}&lt;br&gt;
The Golden Rule&lt;br&gt;
Accept interfaces, return structs.&lt;/p&gt;

&lt;p&gt;Functions orchestrate.&lt;/p&gt;

&lt;p&gt;Methods define behavior on types.&lt;/p&gt;

&lt;p&gt;Interfaces abstract the what from the how.&lt;/p&gt;

&lt;p&gt;Your Turn&lt;br&gt;
What's your favorite small interface from the standard library?&lt;br&gt;
io.Reader? fmt.Stringer? http.Handler?&lt;/p&gt;

&lt;p&gt;Drop it below 👇&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Audit Logs: The Silent Guardian of Every Serious System</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Sat, 23 May 2026 01:54:21 +0000</pubDate>
      <link>https://dev.to/kev_luciano/audit-logs-the-silent-guardian-of-every-serious-system-3d4g</link>
      <guid>https://dev.to/kev_luciano/audit-logs-the-silent-guardian-of-every-serious-system-3d4g</guid>
      <description>&lt;p&gt;You build the feature. You test it. It works.&lt;/p&gt;

&lt;p&gt;Three months later: data is missing, a transaction failed, a user denies doing something your system says they did.&lt;/p&gt;

&lt;p&gt;Where do you look?&lt;/p&gt;

&lt;p&gt;The audit log. And if you don't have one, you're blind.&lt;/p&gt;

&lt;p&gt;What is an audit log?&lt;/p&gt;

&lt;p&gt;A chronological, tamper-evident record of every significant action: who did what, when, and what changed.&lt;/p&gt;

&lt;p&gt;Junior developers think audit logs are a compliance checkbox.&lt;/p&gt;

&lt;p&gt;That thinking is backwards.&lt;/p&gt;

&lt;p&gt;Audit logs aren't for regulators. They're infrastructure for trust. They answer the hardest question any system faces: "What actually happened?"&lt;/p&gt;

&lt;p&gt;8 non-negotiables every developer must know:&lt;/p&gt;

&lt;p&gt;An audit log is NOT a regular log. Never mix them.&lt;/p&gt;

&lt;p&gt;Every entry must answer: Who? Did what? To what? When? From where? Result?&lt;/p&gt;

&lt;p&gt;Timestamps in UTC. Always. No exceptions.&lt;/p&gt;

&lt;p&gt;Append-only. No UPDATE. No DELETE. Ever.&lt;/p&gt;

&lt;p&gt;Use cryptographic hash chaining for tamper-evidence.&lt;/p&gt;

&lt;p&gt;Never log passwords, tokens, or secrets.&lt;/p&gt;

&lt;p&gt;Define your retention policy before you write the first record.&lt;/p&gt;

&lt;p&gt;Treat audit logs as evidence — because someday they will be.&lt;/p&gt;

&lt;p&gt;Audit logs feel unnecessary until they're the only thing standing between you and a disaster you cannot explain.&lt;/p&gt;

&lt;p&gt;Build them seriously from the start.&lt;/p&gt;

&lt;p&gt;Build them as an afterthought, and you will regret it.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>📂 The Hidden Superpower of Go's io/fs: Why Your Code Deserves a Universal Remote Control</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Mon, 16 Mar 2026 18:46:46 +0000</pubDate>
      <link>https://dev.to/kev_luciano/the-hidden-superpower-of-gos-iofs-why-your-code-deserves-a-universal-remote-control-12h7</link>
      <guid>https://dev.to/kev_luciano/the-hidden-superpower-of-gos-iofs-why-your-code-deserves-a-universal-remote-control-12h7</guid>
      <description>&lt;p&gt;🎬 The Moment Everything Clicked&lt;br&gt;
Picture this: It's 2 AM. I'm debugging a Go program that processes text files. My tests keep failing because I forgot to delete a test file from the previous run. Again.&lt;/p&gt;

&lt;p&gt;"You know what?" I muttered to my monitor, "There HAS to be a better way."&lt;/p&gt;

&lt;p&gt;Turns out, there was. And it had been sitting in the standard library all along.&lt;/p&gt;

&lt;p&gt;📖 The Tale of Two File Readers&lt;br&gt;
Let me tell you a story about two developers: Alice and Bob.&lt;/p&gt;

&lt;p&gt;Bob's Approach: The Direct Route&lt;br&gt;
go&lt;br&gt;
// Bob's code - seems simple enough&lt;br&gt;
func ProcessFile(path string) error {&lt;br&gt;
    data, err := os.ReadFile(path)&lt;br&gt;
    if err != nil {&lt;br&gt;
        return err&lt;br&gt;
    }&lt;br&gt;
    // ... process data&lt;br&gt;
    return nil&lt;br&gt;
}&lt;br&gt;
Bob is happy. His code works. Life is good.&lt;/p&gt;

&lt;p&gt;Until...&lt;/p&gt;

&lt;p&gt;He needs to test: "Now I need to create real files for testing 😰"&lt;/p&gt;

&lt;p&gt;He needs to read from an embedded file: "Wait, I can't use os.ReadFile for that 😱"&lt;/p&gt;

&lt;p&gt;He needs to read from a ZIP: "Do I rewrite everything? 😭"&lt;/p&gt;

&lt;p&gt;Alice's Approach: The Universal Remote&lt;br&gt;
go&lt;br&gt;
// Alice's code - works ANYWHERE&lt;br&gt;
func ProcessFile(fsys fs.FS, path string) error {&lt;br&gt;
    data, err := fs.ReadFile(fsys, path)&lt;br&gt;
    if err != nil {&lt;br&gt;
        return err&lt;br&gt;
    }&lt;br&gt;
    // ... process data (same code!)&lt;br&gt;
    return nil&lt;br&gt;
}&lt;br&gt;
Alice's code is like a universal remote that works with any TV:&lt;/p&gt;

&lt;p&gt;Real files? ProcessFile(os.DirFS("."), "input.txt")&lt;/p&gt;

&lt;p&gt;Testing? ProcessFile(fstest.MapFS{...}, "input.txt")&lt;/p&gt;

&lt;p&gt;Embedded? ProcessFile(embed.FS, "input.txt")&lt;/p&gt;

&lt;p&gt;ZIP files? ProcessFile(zipFS, "input.txt")&lt;/p&gt;

&lt;p&gt;SAME CODE. EVERY TIME.&lt;/p&gt;

&lt;p&gt;🎮 Let's Play: Spot the Difference&lt;br&gt;
I'm going to show you two code snippets. Your job? Spot which one will make your life easier in the long run.&lt;/p&gt;

&lt;p&gt;Round 1: Reading a Config File&lt;br&gt;
Option A (The Traditionalist):&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
func LoadConfig() (*Config, error) {&lt;br&gt;
    data, err := os.ReadFile("./config.json")&lt;br&gt;
    if err != nil {&lt;br&gt;
        return nil, fmt.Errorf("failed to read config: %w", err)&lt;br&gt;
    }&lt;br&gt;
    // parse config...&lt;br&gt;
}&lt;br&gt;
Option B (The Visionary):&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
func LoadConfig(fsys fs.FS) (*Config, error) {&lt;br&gt;
    data, err := fs.ReadFile(fsys, "config.json")&lt;br&gt;
    if err != nil {&lt;br&gt;
        return nil, fmt.Errorf("failed to read config: %w", err)&lt;br&gt;
    }&lt;br&gt;
    // parse config...&lt;br&gt;
}&lt;br&gt;
See the difference? Option B doesn't care WHERE the file comes from. It just needs A file system.&lt;/p&gt;

&lt;p&gt;🧪 The Testing Revelation&lt;br&gt;
This is where io/fs goes from "interesting" to "I CAN NEVER GO BACK".&lt;/p&gt;

&lt;p&gt;Testing Bob's Code:&lt;br&gt;
go&lt;br&gt;
func TestBobProcessor(t *testing.T) {&lt;br&gt;
    // Step 1: Create a real file (messy)&lt;br&gt;
    os.WriteFile("test.txt", []byte("hello"), 0644)&lt;br&gt;
    defer os.Remove("test.txt") // Hope we don't forget!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Step 2: Run the test
err := BobProcessor.ProcessFile("test.txt")

// Step 3: Clean up (if we remembered)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
Problems:&lt;/p&gt;

&lt;p&gt;🐢 Slow (actual disk I/O)&lt;/p&gt;

&lt;p&gt;🧹 Need cleanup code&lt;/p&gt;

&lt;p&gt;🔒 Permission issues&lt;/p&gt;

&lt;p&gt;📁 Leftover files when tests crash&lt;/p&gt;

&lt;p&gt;Testing Alice's Code:&lt;br&gt;
go&lt;br&gt;
func TestAliceProcessor(t *testing.T) {&lt;br&gt;
    // Step 1: Create a VIRTUAL file system (magic!)&lt;br&gt;
    mockFS := fstest.MapFS{&lt;br&gt;
        "test.txt": {Data: []byte("hello")},&lt;br&gt;
    }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Step 2: Run the test (NO FILES CREATED!)
err := AliceProcessor.ProcessFile(mockFS, "test.txt")

// Step 3: That's it! Nothing to clean up!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
Benefits:&lt;/p&gt;

&lt;p&gt;⚡ Blazing fast (pure memory)&lt;/p&gt;

&lt;p&gt;🧼 No cleanup needed&lt;/p&gt;

&lt;p&gt;🔒 No permissions to worry about&lt;/p&gt;

&lt;p&gt;🏃‍♂️ Tests can run in parallel safely&lt;/p&gt;

&lt;p&gt;🎯 The "Aha!" Moment&lt;br&gt;
Here's when I truly understood the power of io/fs:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
// My text processor now works ANYWHERE&lt;br&gt;
type TextProcessor struct {&lt;br&gt;
    fsys fs.FS  // The file system to use&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;func NewTextProcessor(fsys fs.FS) *TextProcessor {&lt;br&gt;
    return &amp;amp;TextProcessor{fsys: fsys}&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// In production: use real files&lt;br&gt;
prod := NewTextProcessor(os.DirFS("./data"))&lt;/p&gt;

&lt;p&gt;// In tests: use virtual files&lt;br&gt;
test := NewTextProcessor(fstest.MapFS{&lt;br&gt;
    "input.txt": {Data: []byte("1E (hex)")},&lt;br&gt;
})&lt;/p&gt;

&lt;p&gt;// In CLI tool: let user specify&lt;br&gt;
cli := NewTextProcessor(os.DirFS(userSpecifiedPath))&lt;/p&gt;

&lt;p&gt;// In web server: read from embedded static files&lt;br&gt;
web := NewTextProcessor(embeddedStaticFiles)&lt;/p&gt;

&lt;p&gt;// In cloud: read from S3 (if you implement fs.FS for S3)&lt;br&gt;
cloud := NewTextProcessor(s3fs.New("my-bucket"))&lt;br&gt;
ONE PROCESSOR. INFINITE POSSIBILITIES.&lt;/p&gt;

&lt;p&gt;🎨 The Architecture Diagram (ASCII Art Edition)&lt;br&gt;
text&lt;br&gt;
┌─────────────────────────────────────┐&lt;br&gt;
│      YOUR APPLICATION CODE          │&lt;br&gt;
│    (Works with ANY fs.FS!)          │&lt;br&gt;
└─────────────┬───────────────────────┘&lt;br&gt;
              │&lt;br&gt;
              ▼&lt;br&gt;
┌─────────────────────────────────────┐&lt;br&gt;
│         fs.FS Interface              │&lt;br&gt;
│         "Give me files!"              │&lt;br&gt;
└─────────────┬───────────────────────┘&lt;br&gt;
              │&lt;br&gt;
    ┌─────────┴─────────┐&lt;br&gt;
    │                   │&lt;br&gt;
    ▼                   ▼&lt;br&gt;
┌────────────┐    ┌────────────┐&lt;br&gt;
│ os.DirFS   │    │fstest.MapFS│&lt;br&gt;
│ (Real Disk)│    │(In Memory) │&lt;br&gt;
└────────────┘    └────────────┘&lt;br&gt;
    │                   │&lt;br&gt;
    ▼                   ▼&lt;br&gt;
┌────────────┐    ┌────────────┐&lt;br&gt;
│  Files on  │    │ Virtual    │&lt;br&gt;
│  your HD   │    │ files for  │&lt;br&gt;
│            │    │ testing    │&lt;br&gt;
└────────────┘    └────────────┘&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AND SO MUCH MORE!
     │
     ▼
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;┌────────────────────┐&lt;br&gt;
│ embed.FS           │&lt;br&gt;
│ (Files in binary)  │&lt;br&gt;
├────────────────────┤&lt;br&gt;
│ zip.Reader         │&lt;br&gt;
│ (ZIP archives)     │&lt;br&gt;
├────────────────────┤&lt;br&gt;
│ Custom FS          │&lt;br&gt;
│ (Your imagination!)│&lt;br&gt;
└────────────────────┘&lt;br&gt;
💡 The "Wait, What?" Moments&lt;br&gt;
Moment 1: "But I need to WRITE files!"&lt;br&gt;
Yes, io/fs is READ-ONLY. And that's PERFECT:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
// READ with fs.FS (flexible)&lt;br&gt;
data, _ := fs.ReadFile(myFS, "input.txt")&lt;/p&gt;

&lt;p&gt;// WRITE with os (still need this)&lt;br&gt;
os.WriteFile("output.txt", data, 0644)&lt;br&gt;
The separation is beautiful: reading is abstract, writing is concrete.&lt;/p&gt;

&lt;p&gt;Moment 2: "What about paths on Windows?"&lt;br&gt;
io/fs uses forward slashes (/) everywhere. ALWAYS. Even on Windows:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
// DON'T do this (platform-specific)&lt;br&gt;
path := filepath.Join("folder", "file.txt")&lt;/p&gt;

&lt;p&gt;// DO this (works everywhere with fs.FS)&lt;br&gt;
path := "folder/file.txt"  // Always use /&lt;br&gt;
Moment 3: "Can I use this in MY project?"&lt;br&gt;
YES! And here's how to start:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
// Step 1: Change your function signatures&lt;br&gt;
// FROM:&lt;br&gt;
func DoSomething(filename string)&lt;br&gt;
// TO:&lt;br&gt;
func DoSomething(fsys fs.FS, filename string)&lt;/p&gt;

&lt;p&gt;// Step 2: Use fs.ReadFile instead of os.ReadFile&lt;br&gt;
// Step 3: Pass in the right FS for each use case&lt;br&gt;
🚀 The Challenge&lt;br&gt;
I dare you to refactor ONE of your existing Go projects to use io/fs. Just one function. See how it feels.&lt;/p&gt;

&lt;p&gt;Here's a starter template:&lt;/p&gt;

&lt;p&gt;go&lt;br&gt;
package main&lt;/p&gt;

&lt;p&gt;import (&lt;br&gt;
    "io/fs"&lt;br&gt;
    "os"&lt;br&gt;
    "testing/fstest"&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;// Your refactored function&lt;br&gt;
func MyFunction(fsys fs.FS, path string) (string, error) {&lt;br&gt;
    data, err := fs.ReadFile(fsys, path)&lt;br&gt;
    if err != nil {&lt;br&gt;
        return "", err&lt;br&gt;
    }&lt;br&gt;
    return string(data), nil&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;func main() {&lt;br&gt;
    // Real usage&lt;br&gt;
    result, _ := MyFunction(os.DirFS("."), "real.txt")&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Test usage (in real tests, not main!)
mockFS := fstest.MapFS{
    "test.txt": {Data: []byte("mock data")},
}
testResult, _ := MyFunction(mockFS, "test.txt")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
Try it. You'll thank me later.&lt;/p&gt;

&lt;p&gt;🎁 The Gift That Keeps Giving&lt;br&gt;
Using io/fs is like:&lt;/p&gt;

&lt;p&gt;🍕 Ordering pizza that can be delivered by ANY restaurant&lt;/p&gt;

&lt;p&gt;🔌 Having a plug that works in ANY country&lt;/p&gt;

&lt;p&gt;🎮 Playing games that work on ANY console&lt;/p&gt;

&lt;p&gt;It's abstraction done RIGHT.&lt;/p&gt;

&lt;p&gt;📢 Join the Revolution&lt;br&gt;
The next time you write a function that reads files, ask yourself:&lt;/p&gt;

&lt;p&gt;"Do I need the REAL file system, or do I need A file system?"&lt;/p&gt;

&lt;p&gt;If the answer is "A file system" (and it almost always is), use fs.FS.&lt;/p&gt;

&lt;p&gt;Your future self (and anyone who tests your code) will thank you.&lt;/p&gt;

&lt;p&gt;💬 Let's Discuss!&lt;br&gt;
Have you used io/fs in your projects?&lt;br&gt;
What creative file systems have you implemented?&lt;br&gt;
Any "aha!" moments with file abstraction?&lt;/p&gt;

&lt;p&gt;Drop a comment below! 👇&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P.S. - The next time you see os.ReadFile in your code, imagine it's 1999 and you're using a dial-up modem. fs.ReadFile is your fiber optic connection to the future. 🚀&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  GoLang #Programming #SoftwareEngineering #CleanCode #DeveloperTips #iofs #GoProgramming
&lt;/h1&gt;

</description>
      <category>go</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>The Surgical Precision of `strings.Cut`: Why You Should Stop Over-Splitting Your Go Code</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Sat, 21 Feb 2026 08:42:28 +0000</pubDate>
      <link>https://dev.to/kev_luciano/the-surgical-precision-of-stringscut-why-you-should-stop-over-splitting-your-go-code-492o</link>
      <guid>https://dev.to/kev_luciano/the-surgical-precision-of-stringscut-why-you-should-stop-over-splitting-your-go-code-492o</guid>
      <description>&lt;p&gt;If you are building text-heavy applications—like the ASCII art generator I’ve been tinkering with—you eventually run into a classic Go dilemma: &lt;strong&gt;Convenience vs. Performance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we need to parse a string, our "Day 1" instinct is almost always to reach for &lt;code&gt;strings.Split&lt;/code&gt;. It’s familiar, it’s friendly, and it’s in every tutorial. But if you’re only looking to divide a string into two parts (like a &lt;code&gt;Key:Value&lt;/code&gt; pair or a &lt;code&gt;Header:Body&lt;/code&gt;), &lt;code&gt;strings.Split&lt;/code&gt; isn't just overkill—it’s a resource hog.&lt;/p&gt;

&lt;p&gt;Since Go 1.18, there has been a better way: &lt;strong&gt;&lt;code&gt;strings.Cut&lt;/code&gt;&lt;/strong&gt;. Let’s look at why this "scalpel" is superior to the "axe" that is &lt;code&gt;Split&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🕵️ The Hidden Cost of &lt;code&gt;strings.Split&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To understand why &lt;code&gt;Split&lt;/code&gt; is heavy, we have to look under the hood at its return type: &lt;code&gt;[]string&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you execute &lt;code&gt;strings.Split(input, ":")&lt;/code&gt;, the Go runtime isn't just finding a character; it’s going on a shopping spree with your RAM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Full Scan:&lt;/strong&gt; It traverses the &lt;em&gt;entire&lt;/em&gt; string to find every possible match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Allocation:&lt;/strong&gt; It creates a brand-new slice to hold the results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Overhead:&lt;/strong&gt; It generates new string headers for every single segment discovered.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re processing a 5MB ASCII art file just to find the "Title" at the very beginning, &lt;code&gt;Split&lt;/code&gt; will still scan all 5 million characters looking for more colons. That is a lot of unnecessary work for your Garbage Collector (GC).&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Enter &lt;code&gt;strings.Cut&lt;/code&gt;: The Performance Hero
&lt;/h2&gt;

&lt;p&gt;Introduced to simplify common parsing patterns, &lt;code&gt;strings.Cut&lt;/code&gt; has a beautifully simple signature:&lt;/p&gt;

&lt;h3&gt;
  
  
  Why it wins on the scoreboard:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero Slice Allocations:&lt;/strong&gt; It returns two substrings and a boolean. No slice is created, meaning &lt;strong&gt;zero extra heap allocation&lt;/strong&gt; for the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Early Exit:&lt;/strong&gt; The moment it finds the &lt;em&gt;first&lt;/em&gt; separator, it stops. If your delimiter is at index 10 of a 10,000-character string, &lt;code&gt;Cut&lt;/code&gt; ignores the other 9,990 characters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner Logic:&lt;/strong&gt; You no longer have to check &lt;code&gt;if len(parts) &amp;gt; 1&lt;/code&gt;. The &lt;code&gt;found&lt;/code&gt; boolean tells you exactly what happened.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠️ Refactoring for Elegance
&lt;/h2&gt;

&lt;p&gt;Moving from &lt;code&gt;Split&lt;/code&gt; to &lt;code&gt;Cut&lt;/code&gt; doesn't just make your app faster; it makes your code more readable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Old" Way (Clunky):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// We only want the first two parts, but we still pay for a slice&lt;/span&gt;
&lt;span class="n"&gt;parts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SplitN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid format"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The "Go 1.18+" Way (Sleek):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Direct, intentional, and allocation-free&lt;/span&gt;
&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"="&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid format"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎓 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;As I continue to build out my career in code, I’ve realized that "Good Go" is about being a good neighbor to the runtime.&lt;/p&gt;

&lt;p&gt;By switching to &lt;code&gt;strings.Cut&lt;/code&gt;, you reduce CPU cycles and keep your memory footprint flat. For an ASCII art program, this means smoother rendering and less lag. For your career, it shows you care about the "How" just as much as the "What."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next time you need to break a string in two, put down the axe. Use the scalpel.&lt;/strong&gt;&lt;/p&gt;




</description>
      <category>go</category>
      <category>z01kisumu</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
