<?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.us-east-2.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 RAG Pipelines Fail on East African Documents — and What I Built to Fix It</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Thu, 02 Jul 2026 09:58:46 +0000</pubDate>
      <link>https://dev.to/kev_luciano/why-rag-pipelines-fail-on-east-african-documents-and-what-i-built-to-fix-it-4g1h</link>
      <guid>https://dev.to/kev_luciano/why-rag-pipelines-fail-on-east-african-documents-and-what-i-built-to-fix-it-4g1h</guid>
      <description>&lt;p&gt;I have been watching AI teams in Nairobi build retrieval systems on Kenyan regulatory and legal documents and hit the same invisible wall. They tune their prompts. They swap embedding models. They adjust retrieval parameters. The answers are still wrong.&lt;/p&gt;

&lt;p&gt;The failure is not in the LLM. It is not in the vector store. It is happening one layer below all of that, before any of it runs.&lt;/p&gt;

&lt;p&gt;It is in the chunking step.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem with generic chunking
&lt;/h2&gt;

&lt;p&gt;Every mainstream RAG framework — LangChain, LlamaIndex, Amazon Bedrock Knowledge Bases — chunks documents the same way: fixed character counts or sentence boundaries. This works reasonably well for English-language enterprise documents. It fails on East African regulatory and legal documents in ways that are hard to debug because the failure is invisible.&lt;/p&gt;

&lt;p&gt;Here is a concrete example. Take a CBK (Central Bank of Kenya) circular. Its structure looks 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;1. Purpose
   This circular provides guidelines on...

2. Scope
   This circular applies to all institutions...

3. Customer Data Protection Requirements
   3.1 All DCPs must implement end-to-end encryption...
   3.2 Customer data must be stored within Kenya...
   3.3 DCPs must appoint a dedicated Data Protection Officer...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A generic chunker splitting at 500 characters will cut somewhere inside section 3. The chunk boundary lands mid-requirement. The embedding model encodes an incomplete regulatory instruction. When a compliance team queries "what are the data protection requirements for DCPs", the retrieval system returns a fragment — missing the subsections that define the actual obligations.&lt;/p&gt;

&lt;p&gt;The LLM fills in the gap with its training data. The answer looks confident and is wrong.&lt;/p&gt;

&lt;p&gt;The same failure mode hits every East African document type:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SACCO loan policies&lt;/strong&gt; — penalty clauses separated from the grace period conditions they govern. A chunk containing "a penalty of 2% per month will be charged" with no surrounding context about when the grace period ends is not a useful retrieval unit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kenyan court judgments&lt;/strong&gt; — the FINDINGS section separated from the ORDER that follows from them. A RAG system that retrieves the order without the findings cannot explain why the court ruled that way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kenyan legislation&lt;/strong&gt; — the CBK Act (Cap. 491) has been amended repeatedly. It contains alphanumeric section numbers: 33N, 33O, 33U, 51B. A generic chunker treating these as arbitrary text has no way to know that &lt;code&gt;33U.&lt;/code&gt; is a section boundary and &lt;code&gt;(1)&lt;/code&gt; is a subsection that should stay inside its parent chunk.&lt;/p&gt;




&lt;h2&gt;
  
  
  The root cause
&lt;/h2&gt;

&lt;p&gt;Generic chunkers do not know what kind of document they are reading. They apply the same strategy to a CBK circular, a SACCO policy, a Kenyan Act, and a Silicon Valley terms-of-service document. Structure that is obvious to any lawyer or compliance officer — numbered sections, ALL-CAPS headings, legislative part headers — is invisible to a chunker that only sees character counts.&lt;/p&gt;

&lt;p&gt;The fix is obvious once you see it: detect the document type first, then apply the correct structural cutting grammar for that type.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building Hekima
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://github.com/the-veez/hekima" rel="noopener noreferrer"&gt;Hekima&lt;/a&gt; to solve this. The name means "wisdom" in Swahili.&lt;/p&gt;

&lt;p&gt;The architecture is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input: document (.txt or .pdf)
          ↓
[ Document Type Detector ]
          ↓
[ Structure-Aware Chunker ]
          ↓
Output: JSON chunks with section labels, token counts, metadata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Detection
&lt;/h3&gt;

&lt;p&gt;Detection is deterministic and stateless. No ML model. Each document type has a set of lexical fingerprints — phrases that appear in that type and not in others.&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="n"&gt;signatures&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DocumentType&lt;/span&gt;&lt;span class="p"&gt;][]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TypeLegislation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"Laws of Kenya"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"An Act of Parliament"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Cap."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Short title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Interpretation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"PRELIMINARY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TypeCBKCircular&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"Central Bank of Kenya"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Governor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Ref. No. CBK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"pursuant to"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Banking Act"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"all institutions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The detector scores each type by counting phrase matches. Minimum score of 2 required — prevents single accidental matches from mislabeling. Ties return &lt;code&gt;TypeUnknown&lt;/code&gt;. The same document always produces the same result.&lt;/p&gt;

&lt;p&gt;One subtlety worth noting: CBK circulars and Kenyan legislation share vocabulary. A CBK circular cites "Central Bank of Kenya" and "Banking Act" because it is issued under those authorities. The CBK Act itself also contains those phrases. The disambiguation comes from score gap — legislation uniquely contains "Laws of Kenya", "An Act of Parliament", "Cap.", "Short title", "Interpretation", and "PRELIMINARY". A real Act scores 5-6 on &lt;code&gt;TypeLegislation&lt;/code&gt; and 1-2 on &lt;code&gt;TypeCBKCircular&lt;/code&gt;. A circular scores 4-5 on &lt;code&gt;TypeCBKCircular&lt;/code&gt; and 0-1 on &lt;code&gt;TypeLegislation&lt;/code&gt;. The gap is decisive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chunking strategies
&lt;/h3&gt;

&lt;p&gt;Each document type has its own splitting strategy:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CBK Circulars — &lt;code&gt;splitByNumberedSections&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Splits at top-level integer sections only. &lt;code&gt;isNumberedSection()&lt;/code&gt; uses rune iteration (not byte indexing) for unicode safety, and explicitly rejects subsections:&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;// "3. Requirements" → new chunk boundary&lt;/span&gt;
&lt;span class="c"&gt;// "3.1 Specific requirement" → stays inside section 3's chunk&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;isNumberedSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;runes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="c"&gt;// must start with digits&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;runes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;unicode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// must be followed by dot&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&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;runes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sc"&gt;'.'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="c"&gt;// must NOT be followed by another digit (that would be a subsection)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&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;runes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;unicode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&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;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt; &lt;span class="o"&gt;&amp;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;Kenyan Legislation — &lt;code&gt;splitLegislation&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was the most interesting to build. The CBK Act (Cap. 491) PDF, extracted via &lt;code&gt;pdftotext -layout&lt;/code&gt;, contains:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A Table of Contents with the same Part and Section patterns as the body — but every TOC entry has dot-leaders (&lt;code&gt;"1. Short title ............... 1"&lt;/code&gt;). Detection: &lt;code&gt;strings.Contains(line, "...")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Part headers with Roman numerals and sub-part suffixes introduced by amendment: &lt;code&gt;Part VIA&lt;/code&gt;, &lt;code&gt;Part VIB&lt;/code&gt;, &lt;code&gt;Part VIC&lt;/code&gt;, &lt;code&gt;Part VID&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Section numbers with uppercase letter suffixes: &lt;code&gt;33N&lt;/code&gt;, &lt;code&gt;33O&lt;/code&gt;, &lt;code&gt;33U&lt;/code&gt;, &lt;code&gt;51B&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repealed sections that match the section header regex but contain only a bracketed repeal notice below &lt;code&gt;minChunkLength&lt;/code&gt; — correctly producing no chunk.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The regex that handles all of this:&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;// Handles: "1.", "4A.", "33U.", "51B."&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sectionHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regexp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MustCompile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`^(\d+[A-Z]*)\.\s+\S`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Handles: "Part I", "Part VIA", "Part VIB"&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;partHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regexp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MustCompile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;`(?i)^Part\s+[IVXLCDM]+(A|B|C|D)?\s+[–-]\s+\S`&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;p&gt;Part identity is preserved in &lt;code&gt;Metadata["part"]&lt;/code&gt; on every section chunk beneath it, so a RAG pipeline can filter or boost by Part without needing a dedicated Part-only chunk.&lt;/p&gt;

&lt;h3&gt;
  
  
  The chunk output
&lt;/h3&gt;

&lt;p&gt;Every chunk carries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.1 All DCPs must implement end-to-end encryption...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;3.2 Customer data must be stored within Kenya..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"section"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3. Customer Data Protection Requirements"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"doc_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cbk_circular"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cbk_circular.pdf"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;token_count&lt;/code&gt; uses a word count × 1.3 heuristic (approximates BPE token counts for English and Swahili prose). Use it to enforce context window limits when batching chunks for an embedding model.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;overlap_words&lt;/code&gt; is available via the API — repeats the last N words of each chunk at the start of the next. Applied as a single post-processing pass after splitting, written once and shared across all document types. Recommended for dense regulatory prose: 15–25 words.&lt;/p&gt;




&lt;h2&gt;
  
  
  The HTTP API
&lt;/h2&gt;

&lt;p&gt;Hekima runs as an HTTP server. Integration into any RAG pipeline is one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://hekima-production.up.railway.app/chunk &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@cbk_circular.pdf"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"overlap_words=20"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response is a JSON array of chunks, ready to feed to any embedding model — Titan Embeddings, OpenAI Embeddings, Cohere Embed, whatever you are using.&lt;/p&gt;

&lt;p&gt;The server runs behind a per-IP rate limiter (10 requests/minute, burst of 5) with structured request logging. The Docker image is 19MB — multi-stage build, non-root user, poppler-utils included for PDF extraction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical choices worth explaining
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why Go?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Single binary. No runtime. No dependency hell. The Docker image is 19MB including PDF extraction. A Python equivalent with FastAPI and dependencies would be 300MB+ just to start. For infrastructure that needs to run reliably in constrained East African deployment environments, that matters.&lt;/p&gt;

&lt;p&gt;Outside stdlib, Hekima has one dependency: &lt;code&gt;golang.org/x/time/rate&lt;/code&gt; for the token bucket rate limiter. Everything else — HTTP server, JSON, multipart parsing, file I/O — is stdlib.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why pdftotext and not a Go PDF library?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pdftotext -layout&lt;/code&gt; preserves the spatial layout of the document, which is critical for section detection. The &lt;code&gt;-layout&lt;/code&gt; flag reconstructs the visual column structure from the PDF's character position data. A pure Go PDF parser that extracts raw text loses this layout information, and section headers become indistinguishable from body text.&lt;/p&gt;

&lt;p&gt;The tradeoff: &lt;code&gt;poppler-utils&lt;/code&gt; must be installed as a system dependency. The Dockerfile handles this. For CLI users it is one &lt;code&gt;apt install&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why deterministic detection and not an ML classifier?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;East African regulatory documents have stable structural conventions that have not changed significantly in decades. "Ref. No. CBK" appears on every CBK circular. "Laws of Kenya" appears on every revised statute. "REPUBLIC OF KENYA" appears in every court judgment header. These are not probabilistic signals — they are definitive identifiers.&lt;/p&gt;

&lt;p&gt;A trained classifier would require a labeled dataset, introduce a dependency on a model file, add latency, and produce probabilistic outputs that need a confidence threshold. The lexical fingerprint approach is faster, requires no training data, produces deterministic results, and is trivially explainable. For this use case it is the right choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is next
&lt;/h2&gt;

&lt;p&gt;The engine covers the five highest-value East African document types. The pattern for adding new types is established and documented in CONTRIBUTING.md — if you work with NTSA forms, KRA notices, or county government circulars and have real document samples, contributions are welcome.&lt;/p&gt;

&lt;p&gt;The live demo is at &lt;strong&gt;&lt;a href="https://hekima-production.up.railway.app" rel="noopener noreferrer"&gt;https://hekima-production.up.railway.app&lt;/a&gt;&lt;/strong&gt; — upload any supported document type and see the chunks.&lt;/p&gt;

&lt;p&gt;The repo is at &lt;strong&gt;&lt;a href="https://github.com/the-veez/hekima" rel="noopener noreferrer"&gt;https://github.com/the-veez/hekima&lt;/a&gt;&lt;/strong&gt; — open source, MIT licensed, CI green.&lt;/p&gt;

&lt;p&gt;If you are building RAG systems on East African documents and this solves a problem you have been fighting, I would like to hear about it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built in Nairobi. Solving African problems with African context.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>ai</category>
      <category>rag</category>
      <category>opensource</category>
    </item>
    <item>
      <title>AI Isn't Replacing Developers. It's Exposing Them.</title>
      <dc:creator>Kevin Nambubbi</dc:creator>
      <pubDate>Fri, 26 Jun 2026 13:46:00 +0000</pubDate>
      <link>https://dev.to/kev_luciano/ai-isnt-replacing-developers-its-exposing-them-4ba7</link>
      <guid>https://dev.to/kev_luciano/ai-isnt-replacing-developers-its-exposing-them-4ba7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The more AI writes your code, the more your understanding becomes your competitive advantage."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When I came across the meme comparing AI-assisted coding to human evolution, I couldn't help but laugh.&lt;/p&gt;

&lt;p&gt;The first image shows a developer confidently walking alongside AI, looking like they've reached the next stage of evolution.&lt;/p&gt;

&lt;p&gt;The second image tells a different story.&lt;/p&gt;

&lt;p&gt;After thirty minutes of debugging, the AI is having a sophisticated discussion while the developer stands there looking like a confused caveman.&lt;/p&gt;

&lt;p&gt;It's funny because it's painfully relatable.&lt;/p&gt;

&lt;p&gt;Almost every developer using AI has experienced both sides of this meme.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Illusion of Instant Expertise
&lt;/h2&gt;

&lt;p&gt;Modern AI tools are remarkable.&lt;/p&gt;

&lt;p&gt;Need an authentication system?&lt;/p&gt;

&lt;p&gt;AI can generate one.&lt;/p&gt;

&lt;p&gt;Need a REST API?&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;Need unit tests?&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;Need Docker configuration?&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;Within minutes you've built features that once required hours of research and implementation.&lt;/p&gt;

&lt;p&gt;This creates a dangerous illusion.&lt;/p&gt;

&lt;p&gt;It feels like you're becoming a significantly better developer simply because you're producing more code.&lt;/p&gt;

&lt;p&gt;But writing code has never been the difficult part of software engineering.&lt;/p&gt;

&lt;p&gt;Thinking is.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production Doesn't Care Who Wrote the Code
&lt;/h2&gt;

&lt;p&gt;Everything works perfectly until it reaches production.&lt;/p&gt;

&lt;p&gt;Then reality begins.&lt;/p&gt;

&lt;p&gt;A customer reports a bug.&lt;/p&gt;

&lt;p&gt;Performance suddenly degrades.&lt;/p&gt;

&lt;p&gt;Memory consumption increases.&lt;/p&gt;

&lt;p&gt;Logs become impossible to understand.&lt;/p&gt;

&lt;p&gt;Eventually someone asks the question every engineer fears:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Why was this implemented this way?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At that moment, AI cannot answer for you.&lt;/p&gt;

&lt;p&gt;You become responsible for every line that was merged into production.&lt;/p&gt;

&lt;p&gt;Whether you wrote it yourself or copied it from an AI assistant doesn't matter.&lt;/p&gt;

&lt;p&gt;Responsibility always belongs to the engineer.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Makes Understanding More Valuable
&lt;/h2&gt;

&lt;p&gt;There's a common fear that AI will replace software developers.&lt;/p&gt;

&lt;p&gt;I don't think that's what's happening.&lt;/p&gt;

&lt;p&gt;Instead, AI is exposing the difference between developers who understand software and developers who merely produce it.&lt;/p&gt;

&lt;p&gt;When AI generates code instantly, typing speed is no longer a competitive advantage.&lt;/p&gt;

&lt;p&gt;Understanding becomes the advantage.&lt;/p&gt;

&lt;p&gt;The developers who will succeed are those who can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Evaluate AI-generated solutions.&lt;/li&gt;
&lt;li&gt;Identify hidden bugs.&lt;/li&gt;
&lt;li&gt;Understand architectural trade-offs.&lt;/li&gt;
&lt;li&gt;Recognize security risks.&lt;/li&gt;
&lt;li&gt;Explain design decisions.&lt;/li&gt;
&lt;li&gt;Debug systems when they fail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those skills cannot be copied and pasted.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Rule for Using AI
&lt;/h2&gt;

&lt;p&gt;I rely on AI every day.&lt;/p&gt;

&lt;p&gt;It's become an essential part of my workflow.&lt;/p&gt;

&lt;p&gt;But before I accept any generated code, I ask myself five questions.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Why does this solution work?
&lt;/h3&gt;

&lt;p&gt;If I can't explain the underlying logic, I probably shouldn't merge it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. What problem is this actually solving?
&lt;/h3&gt;

&lt;p&gt;Sometimes AI solves a different problem than the one I intended.&lt;/p&gt;

&lt;p&gt;Understanding the objective matters more than the implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. What are the trade-offs?
&lt;/h3&gt;

&lt;p&gt;Every solution has compromises.&lt;/p&gt;

&lt;p&gt;Better performance may reduce readability.&lt;/p&gt;

&lt;p&gt;Simpler code may sacrifice flexibility.&lt;/p&gt;

&lt;p&gt;Knowing those trade-offs is what separates engineering from code generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Could I debug this in production?
&lt;/h3&gt;

&lt;p&gt;If the answer is no, I haven't learned enough yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Could I explain it to another developer?
&lt;/h3&gt;

&lt;p&gt;Teaching is one of the best tests of understanding.&lt;/p&gt;

&lt;p&gt;If I can't explain it, I probably don't understand it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Most Dangerous Technical Debt
&lt;/h2&gt;

&lt;p&gt;Technical debt isn't limited to messy codebases.&lt;/p&gt;

&lt;p&gt;AI introduces another form of debt:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mental technical debt.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine AI writes 100 lines of code.&lt;/p&gt;

&lt;p&gt;If you understand every line, you've gained knowledge.&lt;/p&gt;

&lt;p&gt;If you understand only ten, you've borrowed ninety lines of understanding that you'll eventually have to repay.&lt;/p&gt;

&lt;p&gt;Usually during a production incident.&lt;/p&gt;

&lt;p&gt;That's the worst possible time to discover you don't understand your own system.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Should Be Your Junior Developer
&lt;/h2&gt;

&lt;p&gt;The healthiest way I've found to think about AI is this:&lt;/p&gt;

&lt;p&gt;Treat AI as your most productive junior developer.&lt;/p&gt;

&lt;p&gt;It can generate ideas.&lt;/p&gt;

&lt;p&gt;It can draft implementations.&lt;/p&gt;

&lt;p&gt;It can automate repetitive work.&lt;/p&gt;

&lt;p&gt;It can explain unfamiliar concepts.&lt;/p&gt;

&lt;p&gt;But you remain the senior engineer responsible for reviewing, improving, and approving every decision.&lt;/p&gt;

&lt;p&gt;That's where real growth happens.&lt;/p&gt;




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

&lt;p&gt;AI has fundamentally changed software development.&lt;/p&gt;

&lt;p&gt;It has made us faster.&lt;/p&gt;

&lt;p&gt;More productive.&lt;/p&gt;

&lt;p&gt;More efficient.&lt;/p&gt;

&lt;p&gt;But speed without understanding is fragile.&lt;/p&gt;

&lt;p&gt;The developers who will build lasting careers won't simply be those who generate the most code.&lt;/p&gt;

&lt;p&gt;They'll be the ones who combine AI-assisted productivity with deep engineering knowledge and sound judgment.&lt;/p&gt;

&lt;p&gt;AI isn't replacing developers.&lt;/p&gt;

&lt;p&gt;It's revealing who truly understands the systems they're building.&lt;/p&gt;

&lt;p&gt;And in the long run, understanding will always outperform dependency.&lt;/p&gt;




&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;How has AI changed your development workflow?&lt;/p&gt;

&lt;p&gt;Have you adopted any habits that ensure you're still learning instead of becoming dependent on AI?&lt;/p&gt;

&lt;p&gt;I'd love to hear your perspective in the comments.&lt;/p&gt;

</description>
    </item>
    <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>
