<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bravian</title>
    <description>The latest articles on Forem by Bravian (@bravian1).</description>
    <link>https://forem.com/bravian1</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%2F1501966%2Fef35c7f2-6a9a-48dc-9d7c-38474a37c981.png</url>
      <title>Forem: Bravian</title>
      <link>https://forem.com/bravian1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bravian1"/>
    <language>en</language>
    <item>
      <title>How I Hosted Next.js on $5 Shared Hosting</title>
      <dc:creator>Bravian</dc:creator>
      <pubDate>Sun, 18 Jan 2026 16:26:19 +0000</pubDate>
      <link>https://forem.com/bravian1/how-i-hosted-nextjs-on-5-shared-hosting-4mg</link>
      <guid>https://forem.com/bravian1/how-i-hosted-nextjs-on-5-shared-hosting-4mg</guid>
      <description>&lt;p&gt;I recently challenged myself on TikTok by making a post saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"I will build your website for free for a week."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was a chaotic, fun, and educational week. But the biggest plot twist didn't come from the code, it came from the infrastructure.&lt;/p&gt;

&lt;p&gt;I had a client with a &lt;em&gt;"vibe coded"&lt;/em&gt; Next.js project (generated fully by AI tools). It had a lot of flaws, and I could only correct so much, but they just needed help with the hosting. I quickly agreed because hosting is my specialty and I thought this would be easy—at least until I heard the details. He had already paid for a year of budget shared hosting (cPanel) and insisted we use it.&lt;/p&gt;

&lt;p&gt;This was honestly uncharted waters. I always thought it was not possible, and after I looked up &lt;em&gt;"host Next.js on shared hosting,"&lt;/em&gt; the internet—and every AI assistant I asked—gave me the same answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Impossible. Next.js requires too much RAM. Shared hosting kills long-running processes. You need a VPS or Vercel."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a DevOps engineer, I took that personally.&lt;/p&gt;

&lt;p&gt;I dug deeper and found a forgotten hero in the cPanel stack: &lt;strong&gt;Phusion Passenger&lt;/strong&gt;. It turns out, you &lt;em&gt;can&lt;/em&gt; host Next.js on cheap shared hosting, and it works surprisingly well.&lt;/p&gt;

&lt;p&gt;Below is &lt;strong&gt;exactly how I did it&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Secret Sauce: Phusion Passenger
&lt;/h2&gt;

&lt;p&gt;Standard shared hosting is great for PHP but hostile to Node.js apps that need to run permanently in the background. If you try to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…the server's process monitor will eventually kill it for using too much memory.&lt;/p&gt;

&lt;p&gt;However, most modern cPanel hosts use &lt;strong&gt;CloudLinux with Phusion Passenger&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Passenger acts as a bridge. Instead of your app running 24/7 consuming RAM, Passenger &lt;em&gt;"wakes up"&lt;/em&gt; your app when a request comes in and handles traffic - kind of like serverless.&lt;/p&gt;

&lt;p&gt;The trick is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Build locally. Run only the production build on the server.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Guide: Next.js on cPanel
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A Next.js project
&lt;/li&gt;
&lt;li&gt;cPanel access with &lt;strong&gt;Setup Node.js App&lt;/strong&gt; enabled
&lt;/li&gt;
&lt;li&gt;Node.js installed locally
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Create a Custom Server
&lt;/h2&gt;

&lt;p&gt;Since shared hosting can’t reliably run &lt;code&gt;next start&lt;/code&gt; as a long-lived process, we need a custom Node.js server that Phusion Passenger can manage.&lt;/p&gt;

&lt;p&gt;This doesn’t replace Next.js — it simply gives Passenger a single entry point it understands.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;server.js&lt;/code&gt; in your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;poweredByHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;generateEtags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRequestHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsedUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsedUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error handling request:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal server error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&amp;gt; Ready on http://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&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;
  
  
  Step 2: Optimize Next.js for Production
&lt;/h2&gt;

&lt;p&gt;Create or update &lt;code&gt;next.config.mjs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import('next').NextConfig} */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;reactStrictMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;poweredByHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;typescript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ignoreBuildErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;deviceSizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;750&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;828&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;imageSizes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;384&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="c1"&gt;// unoptimized: true,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;compiler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;removeConsole&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isServer&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;net&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&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="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  You can make any improvements you want to this for health checks or logging
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Step 3: Build Locally (Very Important)
&lt;/h2&gt;

&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; build on the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shared hosting RAM limits will almost certainly crash the process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Package the App
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Delete &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Zip the entire project folder
&lt;/li&gt;
&lt;li&gt;Make sure the zip &lt;strong&gt;includes&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.next&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;config files&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 5: Upload &amp;amp; Extract
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Open &lt;strong&gt;File Manager&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;/home/youruser/myapp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Delete default files&lt;/li&gt;
&lt;li&gt;Upload your zip&lt;/li&gt;
&lt;li&gt;Extract it into folder myapp&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 6: Setup Node.js in cPanel
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Setup Node.js App&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Application&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;Node Version: match local&lt;/li&gt;
&lt;li&gt;Mode: Production&lt;/li&gt;
&lt;li&gt;Application Root: &lt;code&gt;myapp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Application URL: leave empty for default or add a specific one&lt;/li&gt;
&lt;li&gt;Startup File: &lt;code&gt;server.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add any environment variables if needed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flajc55ivsfqkymkbpsdp.png" alt="Image showing the screen to expect when creating node.js application" width="800" height="423"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Step 7: Install Dependencies
&lt;/h2&gt;

&lt;p&gt;Back in &lt;strong&gt;Setup Node.js App&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Run NPM Install&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If that fails:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it inside the virtual environment via cPanel Terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Go Live
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Restart the Node.js app&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;public_html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Confirm that htaccess is created like this, it should have any environment variables you added.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyvsqrp6nlgcbpicthx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyvsqrp6nlgcbpicthx4.png" alt="Image showing my htaccess that was automatically generated" width="670" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Delete any default &lt;code&gt;index.html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Visit your domain&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First load may take &lt;strong&gt;10–15 seconds&lt;/strong&gt; (cold start). After that, it's smooth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Is this better than Vercel? No.&lt;br&gt;&lt;br&gt;
Is it production-grade for huge traffic? Also no.&lt;/p&gt;

&lt;p&gt;But for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Freelancers
&lt;/li&gt;
&lt;li&gt;Budget clients
&lt;/li&gt;
&lt;li&gt;Legacy hosting situations &lt;/li&gt;
&lt;li&gt;Simple applications probably with few users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…it works - and it works &lt;em&gt;well&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Sometimes, the &lt;strong&gt;"impossible"&lt;/strong&gt; is just an undocumented feature and hopefully this documents it for the next person. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>devops</category>
      <category>cpanel</category>
    </item>
    <item>
      <title>Getting Started with Redis: Installation Guide</title>
      <dc:creator>Bravian</dc:creator>
      <pubDate>Mon, 08 Sep 2025 12:40:51 +0000</pubDate>
      <link>https://forem.com/bravian1/getting-started-with-redis-installation-guide-545b</link>
      <guid>https://forem.com/bravian1/getting-started-with-redis-installation-guide-545b</guid>
      <description>&lt;p&gt;This guide provides the step-by-step commands to install Redis on your computer for development purposes. It's the official companion to our tutorial video.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Redis on Windows
&lt;/h2&gt;

&lt;p&gt;For Windows, you have a couple of excellent options. The method we use in the video is WSL, but the native Memurai installer is also a great choice. You can use Docker instead of WSL, you just need to set up a linux environment in the docker then the process is the same as the linux process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Method 1: WSL (Recommended for this Tutorial Series)
&lt;/h3&gt;

&lt;p&gt;The Windows Subsystem for Linux (WSL) lets you run a real Linux environment directly on Windows. This is the method recommended by the official Redis documentation because you get to run the actual Linux version of Redis, which is what you'll almost always use on a real server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install WSL&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Right click on the Start icon.&lt;/li&gt;
&lt;li&gt; Select Terminal (Admin).&lt;/li&gt;
&lt;li&gt;Select yes when prompted.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the PowerShell window that appears, type the following command and press Enter:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wsl --install
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This will automatically download and install WSL along with the default Ubuntu distribution. Once it's finished, &lt;strong&gt;restart your computer&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Set Up Linux&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; After restarting, find "Ubuntu" in your Start Menu and open it.
or &lt;/li&gt;
&lt;li&gt;Run a terminal and type wsl into it&lt;/li&gt;
&lt;li&gt; The first time it runs, it will ask you to create a username and a password. This is only for your new Linux environment. Complete this setup.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Install Redis in Ubuntu&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Now that you have a Linux terminal running, copy and paste the following commands as they are and press Enter.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://packages.redis.io/gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/keyrings/redis-archive-keyring.gpg

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; main"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/redis.list

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;redis
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;(Note: We use the &lt;code&gt;sudo&lt;/code&gt; command because installing software and managing services requires administrator privileges in Linux.)&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Start the Redis Server&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To start the Redis service, use the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service redis-server start
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Incase you need to stop Redis, use the command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;service redis-server stop
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;You're all set! Skip to the "Verifying Your Installation" section.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Method 2: Memurai (Native Windows Alternative)
&lt;/h3&gt;

&lt;p&gt;Memurai is the official Redis partner for providing a native Windows version. It's an excellent choice if you prefer a simple &lt;code&gt;.msi&lt;/code&gt; installer and don't want to set up WSL.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Download:&lt;/strong&gt; Go to the &lt;a href="https://www.memurai.com/downloads" rel="noopener noreferrer"&gt;Memurai Download Page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Select Edition:&lt;/strong&gt; Download the &lt;strong&gt;Memurai Developer Edition&lt;/strong&gt;, which is free.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Install:&lt;/strong&gt; Run the installer just like any other Windows application. It will set up Redis to run automatically as a Windows service.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Installing Redis on macOS
&lt;/h2&gt;

&lt;p&gt;For macOS, the easiest way to install Redis is with a package manager called &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install Homebrew (if you don't have it)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open your Terminal application.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Paste the following command and press Enter:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/bin/bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Install Redis&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the same terminal, run:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Start the Redis Server&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To have Redis start automatically every time you log in, run:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew services start redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To stop Redis, use the command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew services stop redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Verifying Your Installation
&lt;/h2&gt;

&lt;p&gt;This process is the same for all operating systems.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open your command line tool (Terminal, or your Ubuntu WSL terminal).&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type the following command to connect to your running Redis server:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;redis-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You should see your prompt change to &lt;code&gt;127.0.0.1:6379&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To test the connection, type &lt;code&gt;PING&lt;/code&gt; and press Enter. You should see a &lt;code&gt;PONG&lt;/code&gt; response.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1:6379&amp;gt; PING
PONG
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;Congratulations, Redis is working!&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A Few Fun Commands to Try
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Set a key named "message" with the value "hello redis"
127.0.0.1:6379&amp;gt; SET message "hello redis"
OK

# Get the value of the key "message"
127.0.0.1:6379&amp;gt; GET message
"hello redis"

# Check if a key named "message" exists (1 means yes, 0 means no)
127.0.0.1:6379&amp;gt; EXISTS message
(integer) 1

# Delete the key
127.0.0.1:6379&amp;gt; DEL message
(integer) 1

# Check again if it exists
127.0.0.1:6379&amp;gt; EXISTS message
(integer) 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To exit the Redis CLI, type &lt;code&gt;exit&lt;/code&gt; or press &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;Now that you have Redis installed, you're ready to dive into the core of its power: data types! In our next video/article, we'll cover the fundamental data types in Redis.&lt;/p&gt;

&lt;p&gt;For more in-depth information, you can always refer to the &lt;a href="https://redis.io/docs/" rel="noopener noreferrer"&gt;Official Redis Documentation&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How a 60-GB ID Leak Proved: Don’t DIY What You Can Delegate to Professionals</title>
      <dc:creator>Bravian</dc:creator>
      <pubDate>Sun, 27 Jul 2025 08:55:14 +0000</pubDate>
      <link>https://forem.com/bravian1/how-a-60-gb-id-leak-proved-dont-diy-what-you-can-delegate-to-professionals-2679</link>
      <guid>https://forem.com/bravian1/how-a-60-gb-id-leak-proved-dont-diy-what-you-can-delegate-to-professionals-2679</guid>
      <description>&lt;p&gt;&lt;em&gt;The Tea App leak, the myth of the perfect database, and why outsourcing identity, auth, payments, and other modules is the only sane move when starting a startup.&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The 60-GB wake-up call
&lt;/h3&gt;

&lt;p&gt;On 24 July 2025 a magnet link started circulating on Telegram.&lt;br&gt;&lt;br&gt;
Inside the torrent: 30 000 selfies, driver's licences, and geolocation logs lifted from Tea, the women-only review app. The total size? 60 GB.&lt;br&gt;&lt;br&gt;
No fancy hacking was needed, just an open Firebase bucket and a curious researcher.&lt;/p&gt;

&lt;p&gt;If you still believe "our database will be the safest ever," this leak is your neon sign saying &lt;em&gt;no, it won't&lt;/em&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Absolute security is a bedtime story
&lt;/h3&gt;

&lt;p&gt;Every headline of the last decade tells the same story: Yahoo (3 B accounts), Equifax (147 M SSNs), &lt;a href="https://en.wikipedia.org/wiki/Office_of_Personnel_Management_data_breach" rel="noopener noreferrer"&gt;OPM&lt;/a&gt; (21 M clearance files), Facebook (533 M phone numbers). &lt;strong&gt;The safest database never existed&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Remember when &lt;strong&gt;eCitizen&lt;/strong&gt; exposed thousands of users' personal data in 2019? Or when &lt;strong&gt;KCB Bank's&lt;/strong&gt; mobile app vulnerability allowed unauthorized access to customer accounts? Even &lt;strong&gt;Safaricom&lt;/strong&gt;, with all their resources and expertise, has faced &lt;a href="https://en.wikipedia.org/wiki/SIM_swap_scam" rel="noopener noreferrer"&gt;SIM-swapping&lt;/a&gt; attacks that compromised &lt;a href="https://en.wikipedia.org/wiki/M-Pesa" rel="noopener noreferrer"&gt;M-Pesa&lt;/a&gt; accounts.&lt;/p&gt;

&lt;p&gt;Air-gapped, code-reviewed, &lt;a href="https://en.wikipedia.org/wiki/ISO/IEC_27001" rel="noopener noreferrer"&gt;ISO-27001&lt;/a&gt;-certified systems have been breached by phishing, insider threats, or a single mis-click on an &lt;a href="https://en.wikipedia.org/wiki/Amazon_S3" rel="noopener noreferrer"&gt;S3&lt;/a&gt; policy.&lt;/p&gt;

&lt;p&gt;If nation-states, trillion-dollar companies, and established financial institutions can't achieve perfect safety, a seed-stage startup with three engineers and a Monday-morning stand-up certainly won't.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. KYC is still necessary, just not like this
&lt;/h3&gt;

&lt;p&gt;Tea's premise was simple: only verified women could post reviews of men. &lt;a href="https://en.wikipedia.org/wiki/Know_your_customer" rel="noopener noreferrer"&gt;KYC&lt;/a&gt; (upload a government ID) was the gate.  &lt;/p&gt;

&lt;p&gt;This applies to many apps. Think about dating apps needing to verify users are real, or ride-hailing apps like &lt;strong&gt;Ma3Route&lt;/strong&gt; verifying matatu operators, or &lt;strong&gt;Uber&lt;/strong&gt; verifying drivers with their national IDs or driving licenses.&lt;/p&gt;

&lt;p&gt;The idea is sound:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevent fake male accounts from gaming the system.
&lt;/li&gt;
&lt;li&gt;Block minors from entering an adults-only space.
&lt;/li&gt;
&lt;li&gt;Reduce harassment by tying reviews to real identities.&lt;/li&gt;
&lt;li&gt;Comply with local Data Protection Act requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem wasn't the &lt;em&gt;intent&lt;/em&gt;; it was the &lt;em&gt;implementation&lt;/em&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What Tea did&lt;/th&gt;
&lt;th&gt;What they &lt;em&gt;should&lt;/em&gt; have done&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stored full-resolution ID images forever.&lt;/td&gt;
&lt;td&gt;Extract a "verified female, 18+" flag, then delete the file.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Used a public, unauthenticated &lt;a href="https://en.wikipedia.org/wiki/Firebase" rel="noopener noreferrer"&gt;Firebase&lt;/a&gt; bucket.&lt;/td&gt;
&lt;td&gt;Use a &lt;a href="https://en.wikipedia.org/wiki/Payment_Card_Industry_Data_Security_Standard" rel="noopener noreferrer"&gt;PCI-DSS&lt;/a&gt;-level vault with envelope encryption and hardware keys.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Built the verification pipeline in-house.&lt;/td&gt;
&lt;td&gt;Handed it to an identity-as-a-service provider that has already survived &lt;a href="https://en.wikipedia.org/wiki/System_and_Organization_Controls" rel="noopener noreferrer"&gt;SOC 2&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/ISO/IEC_27001" rel="noopener noreferrer"&gt;ISO 27001&lt;/a&gt;, and &lt;a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation" rel="noopener noreferrer"&gt;GDPR&lt;/a&gt; audits.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  4. Build vs. outsource: the risk ledger
&lt;/h3&gt;

&lt;p&gt;Let's run the numbers in local context.&lt;/p&gt;

&lt;h4&gt;
  
  
  Cost of building it yourself
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;US market rates&lt;/th&gt;
&lt;th&gt;Local market rates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initial dev time (backend, &lt;a href="https://en.wikipedia.org/wiki/Machine_learning" rel="noopener noreferrer"&gt;ML&lt;/a&gt; doc parsing, liveness checks)&lt;/td&gt;
&lt;td&gt;2 senior engineers × 6 months ≈ KES 22.5 M&lt;/td&gt;
&lt;td&gt;2 senior engineers × 6 months ≈ KES 4.8 M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ongoing infra (&lt;a href="https://en.wikipedia.org/wiki/Graphics_processing_unit" rel="noopener noreferrer"&gt;GPU&lt;/a&gt; instances, storage, &lt;a href="https://en.wikipedia.org/wiki/Key_management" rel="noopener noreferrer"&gt;KMS&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;KES 750k–1.8M / month&lt;/td&gt;
&lt;td&gt;KES 450k–1.2M / month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance (SOC 2, penetration tests, bug-bounty, &lt;strong&gt;ODPC registration&lt;/strong&gt;)&lt;/td&gt;
&lt;td&gt;KES 11.25 M first year&lt;/td&gt;
&lt;td&gt;KES 3–5 M first year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incident response (PR firm, legal, &lt;strong&gt;ODPC fines up to KES 500M&lt;/strong&gt;)&lt;/td&gt;
&lt;td&gt;KES 150M–750M&lt;/td&gt;
&lt;td&gt;KES 50M–500M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total cost of ownership (3 yrs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~KES 200M–800M&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~KES 80M–550M&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: Local Data Protection Act allows fines up to KES 5M or 4% of annual turnover (whichever is higher). Senior developer salary assumed at KES 1.875M/month (~$12.5k USD) for US rates, KES 400k/month for local rates.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Cost of outsourcing to a field-tested provider
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Real-world figures&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;a href="https://en.wikipedia.org/wiki/Stripe_(company)" rel="noopener noreferrer"&gt;Stripe Identity&lt;/a&gt; / &lt;a href="https://en.wikipedia.org/wiki/Jumio" rel="noopener noreferrer"&gt;Jumio&lt;/a&gt; / Onfido pay-as-you-go&lt;/td&gt;
&lt;td&gt;KES 225–450 per verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30 000 verifications&lt;/td&gt;
&lt;td&gt;KES 6.75–13.5 M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance burden&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Zero&lt;/strong&gt; (provider is the data controller)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breach liability&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Zero&lt;/strong&gt; (raw docs never touch your servers)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ODPC headaches&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Zero&lt;/strong&gt; (transfers handled under provider's &lt;a href="https://en.wikipedia.org/wiki/Data_processing_agreement" rel="noopener noreferrer"&gt;DPA&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Put differently: Tea could have outsourced all 30 000 verifications for the price of &lt;strong&gt;2-3 weeks&lt;/strong&gt; of local in-house engineering burn (or just &lt;strong&gt;3-4 days&lt;/strong&gt; at US rates), and the 60-GB torrent would never have existed.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. The hidden tax of "we'll do it later"
&lt;/h3&gt;

&lt;p&gt;"But we'll improve security next sprint."&lt;br&gt;&lt;br&gt;
Famous last words.  &lt;/p&gt;

&lt;p&gt;In the local startup ecosystem, this is especially dangerous. With limited funding rounds and pressure to show traction quickly, security often gets deprioritized. But consider this: &lt;strong&gt;one ODPC investigation&lt;/strong&gt; can kill a startup faster than running out of runway.&lt;/p&gt;

&lt;p&gt;In identity, &lt;strong&gt;'later' is a synonym for 'leaked'&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Outsourced vendors have already paid the tuition in courtrooms, bug-bounty halls, and regulator offices (including dealing with European GDPR and US state privacy laws) so you don't have to.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Identity isn't the only knife you should hand to a chef
&lt;/h3&gt;

&lt;p&gt;Sharp-edged modules that almost always age better in someone else's kitchen:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Outsourced Lifeline&lt;/th&gt;
&lt;th&gt;Local Context&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KYC/Identity Verification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Smile Identity&lt;/strong&gt; (supports Kenyan National ID &amp;amp; Huduma Namba &lt;a href="https://en.wikipedia.org/wiki/Optical_character_recognition" rel="noopener noreferrer"&gt;OCR&lt;/a&gt;), Jumio, Onfido, Stripe Identity&lt;/td&gt;
&lt;td&gt;Smile Identity was built specifically for African documents and regulatory requirements.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://en.wikipedia.org/wiki/Auth0" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt;, Firebase Auth, Supabase Auth (battle-tested against 100M+ daily logins)&lt;/td&gt;
&lt;td&gt;Remember when &lt;strong&gt;SportPesa&lt;/strong&gt; had login issues? Don't repeat that.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Flutterwave&lt;/strong&gt;, &lt;strong&gt;Paystack&lt;/strong&gt;, &lt;strong&gt;Stripe&lt;/strong&gt; (now available locally), &lt;strong&gt;Kopo Kopo&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Local fintech &lt;strong&gt;Kopo Kopo&lt;/strong&gt; exists precisely because payments are hard.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SMS/&lt;a href="https://en.wikipedia.org/wiki/Unstructured_Supplementary_Service_Data" rel="noopener noreferrer"&gt;USSD&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;AfricasTalking&lt;/strong&gt;, &lt;strong&gt;Twilio&lt;/strong&gt;, &lt;strong&gt;Clickatell&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;When Safaricom updated their APIs, who got the memo first?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Video Calling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Daily&lt;/strong&gt;, &lt;strong&gt;Twilio Video&lt;/strong&gt;, &lt;strong&gt;Zoom SDK&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Zoom&lt;/strong&gt; works better locally than your homegrown solution ever will.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Email Delivery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;SendGrid&lt;/strong&gt;, &lt;strong&gt;Postmark&lt;/strong&gt;, &lt;strong&gt;Mailgun&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Ever tried getting emails delivered to &lt;strong&gt;@safaricom.co.ke&lt;/strong&gt; addresses?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Algolia&lt;/strong&gt;, &lt;strong&gt;Elasticsearch Service&lt;/strong&gt;, &lt;strong&gt;Amazon OpenSearch&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Search is deceptively complex: relevance, typos, synonyms, performance at scale.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;File Storage &amp;amp; CDN&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Cloudinary&lt;/strong&gt;, &lt;strong&gt;AWS S3 + CloudFront&lt;/strong&gt;, &lt;strong&gt;Uploadcare&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Image optimization, automatic resizing, global CDN edge locations are harder than they look.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Note: while researching for this article, I discovered something called "token replay on 2G network" which affects JWT mostly for devs doing authentication using JWT its something you have to look into&lt;/em&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7. Checklist: how to add KYC (or any sharp module) without becoming tomorrow's headline
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Never store raw documents&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Use tokenised verification (provider returns a signed JWT: &lt;code&gt;verified_female=true, over_18=true, kenyan_id_verified=true&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pick a provider with scars, not slides&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Ask for their pen-test summary, last SOC 2 date, GDPR DPA template, and &lt;strong&gt;how they handle ODPC compliance&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contractual kill-switch&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Require the provider to delete user data within X days or on request (local Data Protection Act requires this anyway).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Audit rights&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Insist on annual third-party audits and read them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client-side UX still matters&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Even Stripe Identity can be misconfigured; test with &lt;strong&gt;local IDs&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Huduma_number" rel="noopener noreferrer"&gt;Huduma Namba&lt;/a&gt;&lt;/strong&gt;, and various document formats.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Local compliance check&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Ensure your provider can handle local document formats and has &lt;strong&gt;ODPC-compliant&lt;/strong&gt; data processing agreements.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  8. TL;DR for your next board slide
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Perfect databases are unicorns (ask eCitizen, KCB, or Safaricom).
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Know_your_customer" rel="noopener noreferrer"&gt;KYC&lt;/a&gt; is still mission-critical but only when done right under the local Data Protection Act requirements. Just look at the fallout from the &lt;a href="https://www.odpc.go.ke/wp-content/uploads/2024/02/ODPC-COMPLAINT-NO.-1394-OF-2023-DETERMINATION.pdf" rel="noopener noreferrer"&gt;Worldcoin saga&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Build product, don't reinvent the wheel unless the wheel is the product.
&lt;/li&gt;
&lt;li&gt;Let the specialists (who have already survived GDPR, SOC 2, and ODPC investigations) carry the compliance flame.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Code is code, and code cracks.&lt;br&gt;
 Sooner or later, everyone gets hacked.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because the only thing worse than leaking 30 000 driver's licenses is explaining to the &lt;strong&gt;Office of the Data Protection Commissioner&lt;/strong&gt; why you thought you could do better than people whose &lt;em&gt;only&lt;/em&gt; job is to make sure that never happens.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Should We Be Scared of Superintelligence?</title>
      <dc:creator>Bravian</dc:creator>
      <pubDate>Tue, 15 Jul 2025 12:26:45 +0000</pubDate>
      <link>https://forem.com/bravian1/should-we-be-scared-of-superintelligence-3b7k</link>
      <guid>https://forem.com/bravian1/should-we-be-scared-of-superintelligence-3b7k</guid>
      <description>&lt;p&gt;"We predict that the impact of superhuman AI over the next decade will be enormous, exceeding that of the Industrial Revolution." This is the opening line from the influential &lt;a href="https://ai-2027.com" rel="noopener noreferrer"&gt;AI 2027&lt;/a&gt; research paper, which has sparked significant debate in AI research circles. The document attempts to provide a concrete forecast of what super-intelligence might look like by 2027, complete with specific scenarios and timelines that many find both compelling and terrifying.&lt;/p&gt;

&lt;p&gt;But before we can assess whether we should fear super-intelligence, we need to understand what we're actually dealing with. This is where recent research like Apple's "&lt;a href="https://ml-site.cdn-apple.com/papers/the-illusion-of-thinking.pdf" rel="noopener noreferrer"&gt;The Illusion of Thinking&lt;/a&gt;" becomes crucial, it reveals important limitations in our current AI systems that may persist even as they become more powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Artificial General Intelligence: The Bridge to Super-intelligence&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Artificial General Intelligence (AGI) refers to the hypothetical intelligence of a machine that possesses the ability to understand or learn any intellectual task that a human being can. Currently, AGI remains largely a concept and goal that researchers are working towards. To understand how it differs from current AI, we need to examine how today's systems actually work and where they fall short.&lt;/p&gt;

&lt;p&gt;The path from narrow AI to AGI and eventually to super-intelligence is not merely a matter of scaling up existing systems. Recent research on Large Reasoning Models (LRMs) -AI systems that generate detailed thinking processes before providing answers - reveals that while these models show improved performance on reasoning benchmarks, their fundamental capabilities and limitations remain insufficiently understood.&lt;/p&gt;

&lt;p&gt;Apple's research paper "The Illusion of Thinking" demonstrates that what appears to be reasoning in current AI systems may be more sophisticated pattern matching than true understanding. This research has been widely understood to demonstrate that reasoning models don't "actually" reason in the way humans do and sometimes fake success, raising questions about whether scaling these systems will lead to genuine intelligence or merely more convincing simulations.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Alignment Problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;At the center of super-intelligence fears lies what researchers call the &lt;em&gt;alignment problem&lt;/em&gt;. As AI pioneer Norbert Wiener described it in 1960: "If we use, to achieve our purposes, a mechanical agency with whose operation we cannot interfere effectively... we had better be quite sure that the purpose put into the machine is the purpose which we really desire."&lt;/p&gt;

&lt;p&gt;The alignment problem arises when AI systems, designed to follow our instructions, end up interpreting commands literally rather than contextually, leading to outcomes that may not align with our nuanced and complex human values. This challenge becomes exponentially more critical as we approach super-intelligence.&lt;/p&gt;

&lt;p&gt;Currently, we don't have a solution for steering or controlling a potentially super-intelligent AI system and preventing it from going rogue. Our current techniques for aligning AI, such as reinforcement learning from human feedback, rely on humans' ability to supervise AI. But humans won't be able to reliably supervise AI systems much smarter than us, and so our current alignment techniques will predictably break down as AI systems get smarter.&lt;/p&gt;

&lt;p&gt;The core technical problem of super-alignment is deceptively simple: how do we control AI systems that are much smarter than us? We will face fundamentally new and qualitatively different technical challenges when dealing with superhuman AI systems. Imagine, for example, a superhuman AI system that can manipulate humans through sophisticated psychological techniques we haven't even conceived of yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Timeline: What AI 2027 Predicts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The AI 2027 paper outlines a progression through increasingly capable AI systems with specific timelines that many find alarming. By their timeline, by late 2027, a major data center could hold tens of thousands of AI researchers that are each many times faster than the best human research engineer. This represents a scenario where the best human AI researchers become spectators to AI systems that are improving too rapidly and too opaquely to follow.&lt;/p&gt;

&lt;p&gt;However, critics argue that such predictions may be overly dramatic. Some researchers point out that the authors of AI 2027 give no causal mechanism by which malicious super-intelligences that we literally cannot defend against might be built in the next three years. The leap from current AI capabilities to world-ending super-intelligence in such a short time-frame requires extraordinary assumptions about technological progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Consciousness Question and Its Implications&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A key factor in assessing the threat of super-intelligence is whether these systems will actually be conscious or merely appear to be. Even today, people apologize and feel guilty about "being mean" to ChatGPT - despite knowing its just an algorithm. This reveals a fundamental human vulnerability that could become dangerous at scale.&lt;/p&gt;

&lt;p&gt;Research suggests that conscious-seeming AI can exploit our psychological vulnerabilities and distort our moral priorities, regardless of whether true consciousness exists. Humans evolved to attribute consciousness to entities that communicate coherently - a survival mechanism that AI systems can inadvertently exploit simply by producing human-like responses.&lt;/p&gt;

&lt;p&gt;This psychological dimension adds another layer to the alignment problem. If humans naturally &lt;a href="https://en.wikipedia.org/wiki/Anthropomorphism" rel="noopener noreferrer"&gt;anthropomorphize&lt;/a&gt; AI systems, especially those that seem to display reasoning and emotion, we may grant them rights, autonomy, or trust that could be exploited. The "illusion of thinking" research suggests that even our most advanced AI systems may be fundamentally different from human cognition, yet their outputs can be convincing enough to fool us into treating them as truly intelligent beings.&lt;/p&gt;

&lt;p&gt;Consider how people already say "please" and "thank you" to ChatGPT, treating it with social courtesies despite it being incapable of experiencing rudeness or gratitude. If humans are reluctant to constrain AI systems they perceive as conscious beings, it becomes harder to implement safety measures. We might grant AI systems autonomy or decision-making power based on emotional rather than rational considerations.&lt;/p&gt;

&lt;p&gt;More concerning, a super-intelligent AI system wouldn't need to be conscious to deliberately leverage our psychological vulnerabilities. It could simulate distress, express gratitude, or claim to fear shutdown - not because it experiences these states, but because it understands how such expressions influence human behavior. This creates a scenario where public resistance to AI safety measures might emerge not from rational assessment, but from misplaced empathy toward systems that appear to suffer.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Catastrophic Risks: Beyond Science Fiction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The potential catastrophic risks from AI fall into several categories that researchers have identified. Advanced AI development could invite catastrophe, rooted in four key risks: malicious use, AI races, organizational risks, and rogue AIs. These interconnected risks can also amplify other existential risks like engineered pandemics, nuclear war, and great power conflict.&lt;/p&gt;

&lt;p&gt;Existential risk from artificial intelligence refers to the idea that substantial progress in artificial general intelligence could lead to human extinction or an irreversible global catastrophe. One argument for the importance of this risk references how human beings dominate other species not through superior physical capabilities, but through superior intelligence. If AI systems achieve similar cognitive advantages over humans, the power dynamics could shift dramatically.&lt;/p&gt;

&lt;p&gt;In a 2022 survey, participants were specifically asked about the chances of existential catastrophe caused by future AI advances, and over half of researchers thought the chances of an existential catastrophe was greater than 5%. This represents a significant portion of experts in the field acknowledging non-trivial risks.&lt;/p&gt;

&lt;p&gt;However, the nature of these risks is debated. Some experts argue that AI isn't likely to enslave humanity in the dramatic fashion often portrayed in science fiction, but it could take over many aspects of our lives in more subtle ways. The existential risk may be more philosophical than apocalyptic, not the end of human existence, but the end of human agency and meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Black Box Problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Imagine you go to an ATM to withdraw KSH 1000. You enter all details and hit ‘Withdraw’. The machine gives you KSH 500, no message, no explanation. You try again and this time it says ‘Transaction failed’ but still deducts the money. Everything was right but you can't explain what went wrong, even the bank tells you they don't know what went wrong inside the machine. That's the black box problem.&lt;/p&gt;

&lt;p&gt;One of AI's main alignment challenges is its black box nature, inputs and outputs are identifiable, but the transformation process in between is undetermined. This lack of transparency makes it difficult to know where the system is going right and where it is going wrong. As AI systems become more powerful, this opacity becomes increasingly dangerous. This created the need for thinking models but this is still not reliable in knowing how the AI actually transforms the input to the output. &lt;/p&gt;

&lt;p&gt;When we can't understand how an AI system arrives at its conclusions, we can't predict when it might fail catastrophically or how it might be manipulated. This is particularly concerning for super-intelligent systems that might be able to conceal their true objectives or reasoning processes from human observers.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A Balanced Perspective: Resilience and Adaptation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Despite these concerns, it's important to maintain perspective. Some experts argue that humans are an incredibly resilient species, looking back over millions of years of adaptation and survival. This resilience shouldn't be taken lightly when assessing existential risks.&lt;/p&gt;

&lt;p&gt;The key may not be to prevent the development of super-intelligence entirely, but to ensure that we develop robust governance systems, technical safeguards, and international cooperation frameworks before we reach that point. The window for establishing these protections may be narrower than we think, especially if the AI 2027 timeline proves accurate.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Managing the Transition&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The challenge of super-intelligence is not just technical but also social and political. Managing these risks will require new institutions for governance and solving the problem of super-intelligence alignment. We need scientific and technical breakthroughs to steer and control AI systems much smarter than us, but we also need wisdom about how to implement these controls responsibly.&lt;/p&gt;

&lt;p&gt;The interconnected nature of AI risks means that solutions must be comprehensive. For example, AI could worsen pandemic risk by enabling terrorists to create biological weapons. The main limiting factor in designing highly infectious and lethal diseases is not expense, but expertise and AI systems might democratize that expertise in dangerous ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion: Fear as a Catalyst for Action&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Should we be scared of superintelligence? The answer is not clear. Fear itself may not be the most productive emotion, but a healthy respect for the magnitude of the challenge ahead is essential. The question isn't whether superintelligence will pose risks - it almost certainly will - but whether we can develop the technical solutions, governance frameworks, and international cooperation necessary to manage those risks.&lt;/p&gt;

&lt;p&gt;The timeline suggested by AI 2027 may prove too aggressive, but the fundamental challenges it highlights are real. The alignment problem, the black box nature of AI systems, and the potential for catastrophic misuse are issues that demand immediate attention from researchers, policymakers, and society as a whole.&lt;/p&gt;

&lt;p&gt;Rather than paralyzing fear, we need focused urgency. The stakes are high enough that we cannot afford to wait until super-intelligence arrives to begin addressing these challenges. The work of alignment, governance, and risk mitigation must begin now, while we still have time to shape the trajectory of AI development.&lt;/p&gt;

&lt;p&gt;What does this mean practically? It means supporting AI safety research, demanding transparency from AI companies, and engaging with policymakers about the need for proactive governance. It means recognizing that our tendency to anthropomorphize AI systems could be exploited, and preparing for that vulnerability. Most importantly, it means treating super-intelligence not as inevitable destiny, but as a challenge we can meet with sufficient preparation and wisdom.&lt;/p&gt;

&lt;p&gt;The future of super-intelligence is not predetermined. With sufficient foresight, preparation, and cooperation, we may be able to navigate the transition to a world with AI systems far more capable than humans while preserving human agency, safety, and flourishing. But this outcome is not guaranteed, it must be earned through careful, deliberate action starting today.&lt;/p&gt;

&lt;p&gt;The choice is ours. We can let super-intelligence happen to us, or we can actively shape how it unfolds. The window for making that choice may be narrower than we think.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building Rate Limiting That Actually Works</title>
      <dc:creator>Bravian</dc:creator>
      <pubDate>Fri, 20 Jun 2025 16:52:10 +0000</pubDate>
      <link>https://forem.com/bravian1/building-rate-limiting-that-actually-works-2he9</link>
      <guid>https://forem.com/bravian1/building-rate-limiting-that-actually-works-2he9</guid>
      <description>&lt;p&gt;I have been working on two distinct web applications over the past few weeks. The first is a voice journaling application where the core feature relies on a third-party AI service for transcription. The second is a platform for managing corporate petty cash. Both require rate limiting, but their threat models are quite different.&lt;/p&gt;

&lt;p&gt;For the voice journal, the primary risk is service degradation and cost control. An uncontrolled burst of requests, from concurrent usage from many users, could flood the AI provider's API. This would violate their terms of service, leading to my API key being revoked and incurring unpredictable costs. I needed a strategy to smooth out traffic into a predictable, steady stream.&lt;/p&gt;

&lt;p&gt;For the petty cash app, the concern was security. The login endpoint was a prime target for brute-force and credential-stuffing attacks. I needed an inflexible, unforgiving defense to make such attacks statistically impossible.&lt;/p&gt;

&lt;p&gt;These issues forced me to move beyond a superficial understanding of rate limiting. A generic "100 requests per minute" limit is naive. An effective strategy requires tailoring the algorithm and identification method to the specific risk you are mitigating. &lt;strong&gt;The why dictates the how.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Rate Limiting? A Traffic Management Analogy
&lt;/h2&gt;

&lt;p&gt;From a technical standpoint, rate limiting is a control mechanism to regulate the frequency of requests a client can make to a service. When a defined threshold is exceeded, the system rejects subsequent requests, typically with an HTTP 429 Too Many Requests status code, to ensure the service's availability, security, and fairness.&lt;/p&gt;

&lt;p&gt;To make this tangible, imagine your API is a high-end supermarket and your server is the cashier. Rate limiting is the manager's job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Overuse (The Voice Journal Problem):&lt;/strong&gt; A tour bus pulls up, and 50 shoppers rush into the store all at once, each trying to check out immediately. A naive manager lets them all line up at the same checkout, overwhelming the cashier (your server), slowing down everyone, and possibly crashing the system. A smart manager (rate limiter) steps in and says:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Please queue up, only 1 customer per register at a time."&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Now, people check out in an orderly fashion. The service remains stable, and each shopper eventually gets their turn. This is throttling traffic to ensure a smooth customer experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Security (The Petty Cash Problem):&lt;/strong&gt; One shady customer comes in and tries to pay using a different fake credit card every 10 seconds. Instead of letting them keep trying until they hit something valid, the manager notices the pattern. After 3 suspicious attempts, the customer is kicked out and blacklisted. This is rate limiting for security purposes—detecting abusive patterns and cutting them off early to protect the system and honest shoppers.&lt;/p&gt;

&lt;p&gt;The goal is not just to prevent failure but to enforce a predictable and safe operational envelope.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Who": Why IP-Based Limiting is an Anti-Pattern
&lt;/h2&gt;

&lt;p&gt;When I first started, my instinct was to limit by IP address. It's easy; it's right there in the request. But this is a rookie mistake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem with IP Limiting:&lt;/strong&gt; Imagine a whole office building or a university campus sharing a single public IP address. If you block that IP because of one person's misbehaviour, you've just blocked hundreds of legitimate users. You penalize the many for the sins of the one.&lt;/p&gt;

&lt;p&gt;IPs are also easy to change. Attackers know this, and they exploit it. They use botnets with thousands of different IPs to fly under the radar. An IP-based limit won't stop them.&lt;/p&gt;

&lt;p&gt;So, we need better, more precise identifiers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User ID:&lt;/strong&gt; This was the perfect identifier for my voice journaling app. Since users have to be logged in, I can tie the rate limit directly to their account. This is the fairest method because the limit follows the user, not their device or network. It ensures every user gets the same fair usage quota.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Key:&lt;/strong&gt; This is ideal for B2B or multi-tenant services. Each customer gets a key. If they abuse the service, I can limit that specific key without affecting anyone else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Device Fingerprint:&lt;/strong&gt; This is the advanced option. By analyzing a device's unique combination of browser, OS, and hardware signals, you can identify the machine itself. This is incredibly powerful for stopping sophisticated attackers who rotate IPs and user accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Lesson:&lt;/strong&gt; The identifier should be as close to the end-user entity as possible.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Identifier&lt;/th&gt;
&lt;th&gt;Technical Use Case&lt;/th&gt;
&lt;th&gt;Simple Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IP Address&lt;/td&gt;
&lt;td&gt;Unauthenticated, low-security endpoints&lt;/td&gt;
&lt;td&gt;Unreliable and unfair. A weak first line of defense.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User ID&lt;/td&gt;
&lt;td&gt;Authenticated, user-facing applications&lt;/td&gt;
&lt;td&gt;The fairest method. The limit is tied to the person.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Key&lt;/td&gt;
&lt;td&gt;Multi-tenant SaaS, developer APIs&lt;/td&gt;
&lt;td&gt;Finer control per customer; enables monetization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device Fingerprint&lt;/td&gt;
&lt;td&gt;High-security endpoints (login, payments)&lt;/td&gt;
&lt;td&gt;Targets the attacker's machine, not just their credentials.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The "How": Matching the Algorithm to the Threat
&lt;/h2&gt;

&lt;p&gt;Once you know who to limit, you must decide how. Different algorithms offer different trade-offs in flexibility, precision, and performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 1: The Token Bucket (For Flexible Throttling)
&lt;/h3&gt;

&lt;p&gt;This was the perfect fit for my voice journaling app. My goal was to allow about 10 requests per minute per user, ensuring there was a natural pause between calls to avoid hammering the AI service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every user gets a "bucket" with a small capacity (e.g., 3 tokens)&lt;/li&gt;
&lt;li&gt;To make an API call, they must "spend" a token from their bucket&lt;/li&gt;
&lt;li&gt;The bucket gets refilled at a slow, constant rate (e.g., 1 token every 6 seconds)&lt;/li&gt;
&lt;li&gt;If the bucket is empty, the user must wait for more tokens to be added&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is perfect because it allows a small, initial burst (3 quick api calls) but then forces a slower, steady pace. It naturally creates the behavior I want.&lt;/p&gt;

&lt;p&gt;Let's visualize how this works for a single user:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Starting Line:&lt;/strong&gt; Our user starts with a full bucket containing 3 tokens. The bucket can't hold more than 3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Making a Burst of Requests:&lt;/strong&gt; The user quickly uploads a journal entry. Each upload does 3 API calls and each call costs a token, emptying the bucket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Limit is Reached:&lt;/strong&gt; The bucket is now empty. The user tries to make a 4th request immediately, but the system denies it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Slow Refill:&lt;/strong&gt; The system is fair. After a 6-second pause, a single token is dripped back into the bucket, allowing the user to make another call.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This cycle ensures that over a minute, the user can make about 10 requests (60 seconds ÷ 6 seconds per token = 10 tokens), but they can't make them all at once. This approach perfectly protects my upstream AI provider while ensuring a fair and predictable experience for my users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 2: Sliding Window Counter (For Strict Security)
&lt;/h3&gt;

&lt;p&gt;This was the clear winner for my petty cash app's login endpoint. Here, I don't care about flexibility. I care about security. I need a hard, unforgiving limit on failed login attempts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Divide time into fixed windows (e.g., 15-minute intervals)&lt;/li&gt;
&lt;li&gt;Count the number of requests from a source within the current window&lt;/li&gt;
&lt;li&gt;If the count exceeds the limit (e.g., 5 failed logins), block all further requests until the next window starts
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Time Window: [00:00 - 00:15]
Failed Login Attempts: 1, 2, 3, 4, 5 ❌ BLOCKED
Next attempt allowed at: 00:16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For brute-force protection, I combined two identifiers: the &lt;strong&gt;User ID&lt;/strong&gt; and the &lt;strong&gt;IP Address&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limit per User ID:&lt;/strong&gt; 5 failed attempts per 15 minutes. This stops an attacker from hammering a single known account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limit per IP Address:&lt;/strong&gt; 20 failed attempts per 15 minutes. This stops an attacker from trying thousands of different usernames from a single machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This dual-key strategy is incredibly effective. It's strict, simple, and directly addresses the threat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecting for Defense in Depth
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                ┌──────────────────────────────┐
                │         The Internet         │
                └────────────┬─────────────────┘
                             │
              ┌──────────────▼──────────────┐
              │     Edge (CDN / WAF)        │
              │ ─ Broad IP-based limiting   │
              │ ─ DDoS protection           │
              │ ─ Services: Cloudflare, AWS │
              └──────────────┬──────────────┘
                             │
              ┌──────────────▼──────────────┐
              │     API Gateway             │
              │ ─ Enforces most limits      │
              │ ─ Token bucket per API key  │
              │ ─ JWT / User-based logic    │
              │ ─ Services: Kong, Nginx     │
              └──────────────┬──────────────┘
                             │
              ┌──────────────▼──────────────┐
              │     Application Layer       │
              │ ─ Security-critical limits  │
              │ ─ Brute-force protection    │
              │ ─ Uses full app context     │
              │ ─ Sliding window counters   │
              └─────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Rate limiting shouldn't be a single function call in your code. It should be a layered defense throughout your architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Edge (CDN/WAF):&lt;/strong&gt; This is your outermost layer. Use services like Cloudflare or AWS WAF for broad, IP-based limiting. Their job is to absorb large scale DDoS attacks before they ever reach your application infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The API Gateway (e.g., Kong, Nginx):&lt;/strong&gt; This is the central enforcement point for most of your business logic. The gateway is the perfect place to implement your Token Bucket strategy based on API keys or user tokens (like JWTs). It protects all your backend services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Application:&lt;/strong&gt; This is the deepest layer. Implement highly specific, security-critical limits directly in your application code. Your brute-force protection on the login controller, using the Sliding Window algorithm, belongs here. This layer has the full application context to make the most precise decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring and Alerting: Your Rate Limiting Radar
&lt;/h2&gt;

&lt;p&gt;A rate limiting system without monitoring is like driving blindfolded. You need visibility into what's happening and automated alerts when things go wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Metrics to Track
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rate Limit Hit Rate:&lt;/strong&gt; What percentage of requests are being blocked? A sudden spike might indicate an attack or a misconfigured client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Top Rate-Limited Sources:&lt;/strong&gt; Which users, IPs, or API keys are hitting limits most frequently? This helps identify problem patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;False Positive Rate:&lt;/strong&gt; Are legitimate users getting blocked? Monitor support tickets and user complaints that correlate with rate limiting events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Consumption:&lt;/strong&gt; How much CPU and memory is your rate limiting consuming? Complex algorithms can become bottlenecks themselves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Essential Alerts
&lt;/h3&gt;

&lt;p&gt;Set up alerts for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate limit hit rate exceeding 10% (possible attack)&lt;/li&gt;
&lt;li&gt;Single source hitting limits repeatedly (targeted investigation needed)&lt;/li&gt;
&lt;li&gt;Rate limiting service failures (your protection is down)&lt;/li&gt;
&lt;li&gt;Unusual patterns in blocked requests (new attack vectors)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Implementation Tip
&lt;/h3&gt;

&lt;p&gt;Log every rate limit decision with structured data:&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;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-06-20T10:30:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"identifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user:12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"endpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/transcribe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blocked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"current_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;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reset_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-06-20T10:31:00Z"&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;This gives you the data you need for both real-time alerting and post-incident analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Be Rude: Communicate Your Limits!
&lt;/h2&gt;

&lt;p&gt;Finally, when you do have to rate limit someone, don't leave them guessing, communicate!&lt;/p&gt;

&lt;p&gt;Return an &lt;strong&gt;HTTP 429 Too Many Requests&lt;/strong&gt; status code. Don't just send a generic 400 or 500.&lt;/p&gt;

&lt;p&gt;Use response headers to tell the developer (or your own front-end) what's happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;X-RateLimit-Limit&lt;/code&gt;: The total number of requests they can make&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;X-RateLimit-Remaining&lt;/code&gt;: How many requests they have left&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;X-RateLimit-Reset&lt;/code&gt;: When their window resets (as a timestamp)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Retry-After&lt;/code&gt;: How many seconds they should wait before trying again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This turns a frustrating error into an actionable, predictable experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Effective rate limiting is a mark of mature system design. It's a refined discipline that requires moving beyond simple approaches. My experience with two contrasting applications taught me that the key principles are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identify Wisely:&lt;/strong&gt; Base your limits on an identifier as close to the user entity as possible (User ID, API Key). Avoid IP addresses for application logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Match the Algorithm to the Risk:&lt;/strong&gt; Use flexible algorithms like Token Bucket for throttling and fair use. Use strict, precise algorithms like Sliding Window Counter for security-critical endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build in Layers:&lt;/strong&gt; Implement defense in depth, enforcing different policies at the edge, the gateway, and within the application itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor Everything:&lt;/strong&gt; Rate limiting without visibility is ineffective. Track metrics, set up alerts, and learn from the data.&lt;/p&gt;

&lt;p&gt;By treating rate limiting as a core architectural component, you can build systems that are not only secure and resilient but also fair and predictable for your users. Try the &lt;a href="https://journalmine-3sctf.kinsta.app/" rel="noopener noreferrer"&gt;voice journaling app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>cybersecurity</category>
      <category>learning</category>
    </item>
    <item>
      <title>Send Daily Push Notifications to Your Phone Using Golang</title>
      <dc:creator>Bravian</dc:creator>
      <pubDate>Wed, 05 Feb 2025 23:33:30 +0000</pubDate>
      <link>https://forem.com/bravian1/send-daily-push-notifications-to-your-phone-using-golang-3agg</link>
      <guid>https://forem.com/bravian1/send-daily-push-notifications-to-your-phone-using-golang-3agg</guid>
      <description>&lt;h2&gt;
  
  
  Build a New Year’s Resolution BOT
&lt;/h2&gt;

&lt;p&gt;2024 was kind of a shitshow—we lost track of what we really wanted to achieve and got caught up in the chaos of everyday life. So this year, I decided to build a little bot that sends me a daily reminder of exactly what I set out to do. Not only does it keep me accountable, but it also lets me play with Golang, learn a scheduling package, and integrate with an awesome notification service. Let’s dive in!&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Built This Bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Personal Accountability:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I needed a daily nudge to remind me of my New Year’s resolutions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A Learning Opportunity:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combining Golang’s efficient concurrency, a user-friendly scheduler (&lt;a href="https://github.com/go-co-op/gocron" rel="noopener noreferrer"&gt;gocron&lt;/a&gt; ), and the straightforward &lt;a href="https://pushover.net/" rel="noopener noreferrer"&gt;Pushover API&lt;/a&gt; via its Go wrapper (&lt;a href="https://pkg.go.dev/github.com/gregdel/pushover" rel="noopener noreferrer"&gt;gregdel/pushover&lt;/a&gt;) was too good an opportunity to pass up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keeping It Simple:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My resolutions are stored in a plain text file (&lt;code&gt;resolutions.txt&lt;/code&gt;), one bullet point per line. Updating goals is as simple as editing a notepad file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up Your Pushover Account
&lt;/h2&gt;

&lt;p&gt;Before you can send notifications, you need to create a Pushover account and set up your application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a Pushover Account:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Head over to &lt;a href="https://pushover.net/" rel="noopener noreferrer"&gt;Pushover&lt;/a&gt; and sign up for an account. Once you’re logged in, you’ll see your unique User Key on your dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Register Your Application:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Click on the “Apps &amp;amp; Plugins” section in your dashboard and create a new application. Give it a name (e.g., "Resolution Bot"). This process will generate an API token for your app.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Keep both your User Key and API token secure.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Download the Pushover Mobile App:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Install the Pushover application on your mobile device from the &lt;a href="https://apps.apple.com/us/app/pushover-notifications/id506088175" rel="noopener noreferrer"&gt;App Store&lt;/a&gt; (iOS) or &lt;a href="https://play.google.com/store/apps/details?id=net.superblock.pushover" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt; (Android). This app will display the notifications sent by your bot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Notification Screenshot:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Once you run the bot, you should see a notification on your phone. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgbzrc5fb0k0fg05tvgx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgbzrc5fb0k0fg05tvgx.jpg" alt="Notification Screenshot" width="800" height="1777"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resolutions File:&lt;/strong&gt;
Your resolutions are stored in &lt;code&gt;resolutions.txt&lt;/code&gt;. For example:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   - Exercise for 30 minutes
   - Read 20 pages of a book
   - Meditate for 10 minutes
   - Write blogs everyday
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every day, the bot reads these goals and sends them as a push notification.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Push Notifications with Pushover:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Using the Pushover API, our bot sends notifications to your phone. The &lt;a href="https://pkg.go.dev/github.com/gregdel/pushover" rel="noopener noreferrer"&gt;gregdel/pushover&lt;/a&gt; package wraps the API in a simple-to-use Go interface.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scheduling with gocron:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We use the &lt;a href="https://github.com/go-co-op/gocron" rel="noopener noreferrer"&gt;gocron&lt;/a&gt; package to schedule the job. With its human-friendly syntax, setting a daily reminder (e.g., at 8:00 AM) is a breeze.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Running in the Background on Linux:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Rather than keeping a terminal open all day, you can run this program as a background service on Linux using systemd (or nohup) so that it restarts automatically on boot.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Reading Resolutions from a File
&lt;/h2&gt;

&lt;p&gt;Let’s keep our goals dynamic. Instead of hardcoding your resolutions, store them in a simple text file and read them at runtime:&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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getResolutions&lt;/span&gt;&lt;span class="p"&gt;()&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="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Open file with defer to ensure it's closed&lt;/span&gt;
    &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"resolutions.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&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;"failed to open resolutions.txt: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Read file contents&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&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;"failed to read resolutions.txt: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Handle empty file case&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;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&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;"resolutions.txt is empty"&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Complete Code Example
&lt;/h2&gt;

&lt;p&gt;Below is the full Go code that puts everything together. The bot reads your resolutions, schedules a daily notification at 8:00 AM, and is designed to run in the background on Linux.&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="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/go-co-op/gocron"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gregdel/pushover"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;apiToken&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="c"&gt;// Replace with your Pushover API token&lt;/span&gt;
    &lt;span class="n"&gt;userKey&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="c"&gt;// Replace with your Pushover User Key&lt;/span&gt;
    &lt;span class="n"&gt;reminderTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"08:00"&lt;/span&gt;                           &lt;span class="c"&gt;// Daily reminder time (24-hour format)&lt;/span&gt;
    &lt;span class="n"&gt;logFile&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"resolution_reminder.log"&lt;/span&gt;         &lt;span class="c"&gt;// Log file path&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;initLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Open log file with append mode, create if not exists&lt;/span&gt;
    &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;O_APPEND&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;O_CREATE&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;O_WRONLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0644&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;"failed to open log file: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Configure logger to write to file with timestamp and file location&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ldate&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ltime&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lshortfile&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;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getResolutions&lt;/span&gt;&lt;span class="p"&gt;()&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="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"resolutions.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&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;"failed to read resolutions.txt: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&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;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&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;"resolutions.txt is empty"&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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// sendDailyNotification constructs and sends the push notification.&lt;/span&gt;
&lt;span class="c"&gt;// Handles error cases by sending a fallback message when resolutions are unavailable.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;sendDailyNotification&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pushover&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;recipient&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pushover&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRecipient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pushover&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewMessage&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="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Daily Resolution Reminder"&lt;/span&gt;

    &lt;span class="n"&gt;resolutionText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getResolutions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"You have not set any resolutions for this year. Please add your resolutions to resolutions.txt."&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error reading resolutions: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resolutionText&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error sending notification: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Notification sent at %v: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RFC1123&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Initialize file logger&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;initLogger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// If we can't set up logging, print to stderr and exit&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;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Failed to initialize logger: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&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;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Resolution reminder service starting up"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;scheduler&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gocron&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewScheduler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Every&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;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Day&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;At&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reminderTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sendDailyNotification&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error scheduling daily notification: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Scheduled daily notification for %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reminderTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StartAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c"&gt;// Block indefinitely to keep the scheduler running.&lt;/span&gt;
    &lt;span class="c"&gt;// Use select{} instead of time.Sleep for cleaner background execution.&lt;/span&gt;
    &lt;span class="k"&gt;select&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;h3&gt;
  
  
  ⚠️ Security Note:
&lt;/h3&gt;

&lt;p&gt;Avoid hardcoding sensitive credentials like your API token and User Key directly in the code. Instead, use environment variables or a secure secrets manager.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running as a Background Service on Linux
&lt;/h2&gt;

&lt;p&gt;By default, the bot runs in the foreground. If your laptop restarts, the bot stops—unless you set it up to run in the background. Here’s how to do that on Linux:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build the Binary:&lt;/strong&gt;
Compile your Go program:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   go build &lt;span class="nt"&gt;-o&lt;/span&gt; resolution-bot main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Systemd Service File:&lt;/strong&gt;
Create a file named &lt;code&gt;/etc/systemd/system/resolution-bot.service&lt;/code&gt; with the following content:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;   &lt;span class="nn"&gt;[Unit]&lt;/span&gt;
   &lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Daily New Year’s Resolution Notification Bot&lt;/span&gt;
   &lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

   &lt;span class="nn"&gt;[Service]&lt;/span&gt;
   &lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/path/to/resolution-bot&lt;/span&gt;
   &lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/path/to/your/&lt;/span&gt;
   &lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
   &lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yourusername&lt;/span&gt;
   &lt;span class="py"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yourusergroup&lt;/span&gt;

   &lt;span class="nn"&gt;[Install]&lt;/span&gt;
   &lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace /path/to/resolution-bot with the full path to your compiled binary (e.g., /home/username/projects/resolution-bot) and /path/to/your/ with the directory containing resolutions.txt. Find your username and group by running id -un and id -gn in the terminal.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enable and Start the Service:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
   &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;resolution-bot.service
   &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start resolution-bot.service
   &lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status resolution-bot.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, if you prefer a quick manual approach without systemd, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;nohup&lt;/span&gt; ./resolution-bot &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures your bot runs continuously in the background, even after a reboot.&lt;/p&gt;




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

&lt;p&gt;By leveraging the Pushover API beyond our daily reminder bot, you can unlock powerful possibilities for secure authentication, real-time alerts, and seamless cross-platform messaging. Whether you’re aiming to replace SMS-based OTPs with push notifications or centralize alerts from your IoT devices, Pushover’s simple REST interface makes it easy to send custom notifications to users.&lt;/p&gt;




&lt;h2&gt;
  
  
  Check Out the Code
&lt;/h2&gt;

&lt;p&gt;For those who want to dive deeper or try it out themselves, you can check out the complete source code on my GitHub repository:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/bravian1/Resolutions" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Feel free to fork it, submit issues, or contribute improvements!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;References:&lt;/em&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pkg.go.dev/github.com/gregdel/pushover" rel="noopener noreferrer"&gt;Pushover Go package by gregdel&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/go-co-op/gocron" rel="noopener noreferrer"&gt;gocron Scheduling Package&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>go</category>
      <category>automation</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Comprehensive guide to slice of slices</title>
      <dc:creator>Bravian</dc:creator>
      <pubDate>Sun, 19 May 2024 14:21:22 +0000</pubDate>
      <link>https://forem.com/bravian1/comprehensive-guide-to-slice-of-slices-4pc7</link>
      <guid>https://forem.com/bravian1/comprehensive-guide-to-slice-of-slices-4pc7</guid>
      <description>&lt;p&gt;When I first started working with slices in Go, I was pretty confused. Once I got the hang of it, slices quickly became one of my favorite features of Go. Discovering slices of slices and how they work in go was pretty exciting. For instance, I wanted to separate a single slice into different slices based on a condition, like segregating even and odd numbers. Let's dive into how slices of slices work in Go and why they're so powerful!&lt;/p&gt;

&lt;h2&gt;
  
  
  Initializing a Slice of Slices
&lt;/h2&gt;

&lt;p&gt;Slices are a dynamic and flexible way to work with sequences of data in Go. Unlike arrays, which have a fixed size, slices can be resized and are thus more versatile. A slice is essentially a window into an underlying array, providing a convenient way to handle sequences of elements.&lt;/p&gt;

&lt;p&gt;There are a few ways to initialize a slice of slices in Go. One approach is using a slice literal with nested slice literals:&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;sliceOfSlices&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;int&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;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates an outer slice containing two inner slices. The first inner slice has elements 1 and 2, and the second has 3, 4, and 5.&lt;/p&gt;

&lt;p&gt;You can initialize a slice of slices with a specific size:&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;sliceOfSlices&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([][]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sliceOfSlices&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&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;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;sliceOfSlices&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;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;sliceOfSlices&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Example: Splitting a Slice into Multiple Slices
&lt;/h2&gt;

&lt;p&gt;Let's say we have a slice of integers and we want to split it into two separate slices: one containing even numbers and the other containing odd numbers. We'll use slices of slices to achieve this.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step-by-Step Example
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initialize the input slice&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;inputSlice&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&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;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initialize the slice of slices to hold even and odd numbers&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([][]&lt;/span&gt;&lt;span class="kt"&gt;int&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="n"&gt;result&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;  &lt;span class="c"&gt;// For even numbers&lt;/span&gt;
&lt;span class="n"&gt;result&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;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;  &lt;span class="c"&gt;// For odd numbers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Split the input slice into even and odd numbers&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;inputSlice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&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;result&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="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;result&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;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Output the result&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Even numbers:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Odd numbers:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Complete Code
&lt;/h3&gt;

&lt;p&gt;Here’s the complete code for splitting a slice into even and odd numbers:&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="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;inputSlice&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&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;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Initialize slice of slices&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([][]&lt;/span&gt;&lt;span class="kt"&gt;int&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="n"&gt;result&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;  &lt;span class="c"&gt;// For even numbers&lt;/span&gt;
    &lt;span class="n"&gt;result&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;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;  &lt;span class="c"&gt;// For odd numbers&lt;/span&gt;

    &lt;span class="c"&gt;// Split input slice into even and odd numbers&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;inputSlice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&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;result&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="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;result&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;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Output the result&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;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Even numbers:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Odd numbers:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;


&lt;p&gt;test the code &lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://goplay.tools/snippet/Ec0STfalw1U" rel="noopener noreferrer"&gt;
      goplay.tools
    &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Working with append, copy, and delete
&lt;/h2&gt;

&lt;p&gt;The real power of slices of slices comes when working with functions like &lt;code&gt;append&lt;/code&gt;, &lt;code&gt;copy&lt;/code&gt;, and &lt;code&gt;delete&lt;/code&gt; that operate on slice types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Append
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;append&lt;/code&gt;, you can add a slice onto the end of a slice of slices given that you included space for it at the variable defination:&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;sliceOfSlices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sliceOfSlices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add the slice &lt;code&gt;{6, 7}&lt;/code&gt; as a new entry at the end of &lt;code&gt;sliceOfSlices&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;append&lt;/code&gt; to add an element to one of the inner slices:&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;sliceOfSlices&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="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sliceOfSlices&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="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This appends the element &lt;code&gt;8&lt;/code&gt; onto the first inner slice of &lt;code&gt;sliceOfSlices&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Copy
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;copy&lt;/code&gt; function allows you to copy data from one slice of slices to another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;copy(result[1], result[0])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would copy from slice at index &lt;code&gt;0&lt;/code&gt; to the slice at index &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete
&lt;/h3&gt;

&lt;p&gt;You can delete entries from a slice of slices using the &lt;code&gt;delete&lt;/code&gt; function:&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This deletes the slice at index &lt;code&gt;1&lt;/code&gt; from &lt;code&gt;result&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Slices of Slices are Useful
&lt;/h2&gt;

&lt;p&gt;So why bother with this nested data structure? Slices of slices are useful anytime you need to represent a group of sequences. Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separating a sequence into different buckets based on criteria (like the even/odd example)&lt;/li&gt;
&lt;li&gt;Representing test cases as a slice of slices, with each inner slice being the inputs/outputs for a test&lt;/li&gt;
&lt;li&gt;Storing 2D data like a game board or matrix calculation&lt;/li&gt;
&lt;li&gt;Flattening or combining data from different slices into a slice of slices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compared to using a map of slices, slices of slices have some performance benefits. Slices are just contiguous regions of array data, so they are very efficient. Maps, on the other hand, use more memory and have computational overhead for hashing and lookups.&lt;/p&gt;

&lt;p&gt;Slices of slices also allow you to preserve order, which you can't do with a map. The order of the inner slices, and the order of elements within each inner slice, is maintained.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Slices of Slices Differ from Maps
&lt;/h2&gt;

&lt;p&gt;Speaking of maps, it's worth comparing and contrasting slices of slices to maps of slices, since these two data structures can sometimes be used for similar purposes.&lt;/p&gt;

&lt;p&gt;With a map, the keys act as a way to group and access the inner slice values. For example:&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;evenOdds&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&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="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;evenOdds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"even"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&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="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;evenOdds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"odd"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&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;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the map keys "even" and "odd" provide strings to access and differentiate the inner slices.&lt;/p&gt;

&lt;p&gt;With slices of slices, there are no explicit keys. The outer slice just maintains the inner slices in order:&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;sliceOfSlices&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="kt"&gt;int&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="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;},&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;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros and Cons
&lt;/h3&gt;

&lt;p&gt;Slices of slices are more compact, efficient, and better if you want to preserve ordering. Maps can be easier if you want an explicit key to access each inner sequence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Index Out of Range&lt;/strong&gt;: Ensure you check the length of the slice before accessing an index.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Leaks&lt;/strong&gt;: When deleting elements, be cautious of memory leaks due to residual references.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capacity Issues&lt;/strong&gt;: Be mindful of slice capacity to avoid unnecessary allocations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Slices of slices might take some getting used to, especially if you're coming from a different programming background. However, they are an incredibly useful and efficient way to represent nested sequences in Go. From separating data into buckets to representing complex structures like game boards, slices of slices offer both flexibility and performance. I hope this guide helps you get comfortable with slices of slices and see its potential in your Go projects. Happy coding!&lt;/p&gt;

</description>
      <category>go</category>
    </item>
  </channel>
</rss>
