<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ajisafe Victor Oluwapelumi</title>
    <description>The latest articles on DEV Community by Ajisafe Victor Oluwapelumi (@ajipelumi).</description>
    <link>https://dev.to/ajipelumi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F939563%2Fe786cbf3-74b5-44a6-817d-c50a5b93d2f8.png</url>
      <title>DEV Community: Ajisafe Victor Oluwapelumi</title>
      <link>https://dev.to/ajipelumi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ajipelumi"/>
    <language>en</language>
    <item>
      <title>The recipe you cannot test yourself</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Wed, 30 Sep 2026 09:51:54 +0000</pubDate>
      <link>https://dev.to/ajipelumi/the-recipe-you-cannot-test-yourself-4cad</link>
      <guid>https://dev.to/ajipelumi/the-recipe-you-cannot-test-yourself-4cad</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path Two: Vibe-Code Something Strange&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;A recipe review queue in Sanity Studio where publishing is gated on somebody other than the author having actually cooked the thing.&lt;/p&gt;

&lt;p&gt;Recipe sites publish recipes nobody made. "Approved" usually means one person clicked a button on their own work. I wanted a workflow where that is structurally impossible rather than discouraged by policy.&lt;/p&gt;

&lt;p&gt;A recipe moves Drafting to Testing to Approval to Published. The author writes it and submits. Then a &lt;strong&gt;different person&lt;/strong&gt; has to claim the test, cook it, and write up what happened before it can move on. A failed test sends it back to drafting. An approver sending it back wipes the previous test result, because that pass described a different version of the recipe.&lt;/p&gt;

&lt;p&gt;The whole constraint is one filter in the workflow definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!defined($fields.tester) &amp;amp;&amp;amp; $fields.author.id != $actor.id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That line is the product. Everything else is scaffolding around it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/JpMx_iGFcFg" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Two accounts, side by side. The author submits, tries to test their own recipe, and the action is refused. A second person picks it up, cooks it, reports back, and it publishes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ajipelumi" rel="noopener noreferrer"&gt;
        ajipelumi
      &lt;/a&gt; / &lt;a href="https://github.com/ajipelumi/recipe-review-queue" rel="noopener noreferrer"&gt;
        recipe-review-queue
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Recipe Review Queue&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A Sanity Studio where a recipe cannot be published until somebody &lt;strong&gt;other than
its author&lt;/strong&gt; has actually cooked it.&lt;/p&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16" rel="nofollow"&gt;DEV x Sanity Challenge, Path Two&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The idea&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Recipe sites publish recipes nobody made. "Approved" usually means one person
clicked a button on their own work. This makes that structurally impossible
rather than discouraged by policy.&lt;/p&gt;
&lt;p&gt;A recipe moves through four stages:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;Drafting -&amp;gt; Testing -&amp;gt; Approval -&amp;gt; Published
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;with a rejection at either Testing or Approval looping back to Drafting.&lt;/p&gt;
&lt;p&gt;The constraint that matters is one filter in
&lt;a href="https://github.com/ajipelumi/recipe-review-queue/workflows/recipeReview.ts" rel="noopener noreferrer"&gt;&lt;code&gt;workflows/recipeReview.ts&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-ts notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;filter: &lt;span class="pl-s"&gt;'!defined($fields.tester) &amp;amp;&amp;amp; $fields.author.id != $actor.id'&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The author is excluded from claiming the test. A recipe only reaches Approval
once a second person has cooked it and written up what happened, so "approved"
is a claim backed by evidence rather than a status someone set on their own
work.&lt;/p&gt;
&lt;p&gt;Two supporting decisions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ajipelumi/recipe-review-queue" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  My Build Process
&lt;/h2&gt;

&lt;p&gt;I used Claude Code in the terminal, not an IDE-embedded assistant. The whole thing is a Sanity Studio plus one workflow definition file, so there was very little UI to write and a lot of API surface to get right.&lt;/p&gt;

&lt;h3&gt;
  
  
  What worked: pointing it at the cookbook instead of describing the goal
&lt;/h3&gt;

&lt;p&gt;The prompt that moved fastest was not a description of what I wanted. It was telling it to fetch Sanity's own &lt;a href="https://www.sanity.io/docs/workflows/cookbook-editorial-review" rel="noopener noreferrer"&gt;Editorial review cookbook&lt;/a&gt;&lt;br&gt;
and adapt it. Workflows is early access, so the model's training data does not contain &lt;code&gt;defineWorkflow&lt;/code&gt;, &lt;code&gt;defineStage&lt;/code&gt;, or the shape of an &lt;code&gt;ops&lt;/code&gt; array. Asking it to invent that API from a description produced plausible nonsense. Asking it to read the real page first produced working code.&lt;/p&gt;

&lt;p&gt;Same for the Studio plugin. Rather than guess at the config, it read the type definitions out of &lt;code&gt;node_modules&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;WorkflowPluginConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;workflowDataset&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;WorkflowMapping&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;&lt;code&gt;tag&lt;/code&gt; is the only required field and bindings auto-discover from the deployed definition. That is two minutes of reading that replaced an afternoon of guessing.&lt;/p&gt;

&lt;h3&gt;
  
  
  What did not work: three separate environment failures
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The documented CLI does not exist.&lt;/strong&gt; The cookbook says to deploy with&lt;br&gt;
&lt;code&gt;sanity-workflows deploy&lt;/code&gt;. That package is not on npm. Neither is&lt;br&gt;
&lt;code&gt;@sanity/workflows-cli&lt;/code&gt;. The docs describe a tool that has not shipped. The programmatic path in the same doc does work, so deployment goes through a script instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deployDefinitions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;expectedMinReaderModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;definitions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;recipeReview&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;&lt;strong&gt;Node 20 silently half-installs Sanity 6.&lt;/strong&gt; &lt;code&gt;npm install&lt;/code&gt; kept "succeeding" while leaving &lt;code&gt;package.json&lt;/code&gt; untouched and &lt;code&gt;node_modules/.bin&lt;/code&gt; empty. I went looking for a corrupted install, a bad npm config, a Windows symlink permissions problem. The actual cause was that &lt;code&gt;sanity@6&lt;/code&gt; requires Node 22.12+ and this machine had 20.19.5. Running the binary directly said so in one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: Node.js version &amp;gt;=22.12 required. You are running v20.19.5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;EBADENGINE&lt;/code&gt; warnings had been scrolling past the whole time. I had&lt;br&gt;
dismissed them as the usual noise. Worth checking whether a package requires a newer runtime before assuming the install is broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;sanity deploy&lt;/code&gt; refuses on a permission I appear to have.&lt;/strong&gt; It fails with &lt;code&gt;User is missing required grant sanity.project.deployStudio&lt;/code&gt;. I ruled out the obvious causes in order: the robot token in &lt;code&gt;.env&lt;/code&gt; shadowing my login (re-ran with it unset, same error), my account role (administrator on the project), and the plan (the Manage UI shows an enabled "Add studio" button). Still refused. I stopped there and demoed from &lt;code&gt;npm run dev&lt;/code&gt; instead. The brief asks for a video walkthrough or a link, not specifically a hosted Studio, and honestly the constraint demos better with two browsers side by side than it would on a static URL.&lt;/p&gt;
&lt;h3&gt;
  
  
  Where I nearly fooled myself
&lt;/h3&gt;

&lt;p&gt;I wrote a script to drive an instance through the stages and prove the rule fires. It printed exactly what I wanted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;submit     -&amp;gt; testing
claim test -&amp;gt; REFUSED: action filter returned false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I opened the Studio and looked at the instance. The &lt;code&gt;author&lt;/code&gt; field said &lt;code&gt;g-n9iPHYh2CdfV&lt;/code&gt; -- the API token's robot user. The script authenticates with a token, so the engine saw the robot as author, and the filter had correctly blocked &lt;em&gt;the robot&lt;/em&gt;. I could have cheerfully tested my own recipe on camera and the demo would have proved nothing.&lt;/p&gt;

&lt;p&gt;The engine takes its actor from the token and &lt;code&gt;fireAction&lt;/code&gt; has no actor&lt;br&gt;
override, which means one token is always one person. That is not a limitation to work around. It is the rule doing its job. Demonstrating a two-person constraint genuinely requires two people, so the video uses two real accounts and the script now says so in a comment rather than pretending otherwise.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I would do differently
&lt;/h3&gt;

&lt;p&gt;Check runtime requirements before debugging an install. Read the vendor's own docs into context before asking for code against a pre-release API. And verify &lt;em&gt;who&lt;/em&gt; an automated test is acting as, not just what it printed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Project ID:&lt;/strong&gt; &lt;code&gt;qomqzwv7&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Dataset:&lt;/strong&gt; &lt;code&gt;production&lt;/code&gt; (public)&lt;br&gt;
&lt;strong&gt;Workflow definition:&lt;/strong&gt; &lt;code&gt;recipe-review&lt;/code&gt; v1&lt;/p&gt;

&lt;p&gt;The dataset holds three recipes and the workflow instances. One instance is left at &lt;code&gt;published&lt;/code&gt;, from the run recorded in the video, so the completed path is inspectable rather than just described.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
    <item>
      <title>I tried to catch Next.js contradicting itself. It never did.</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Fri, 25 Sep 2026 08:24:51 +0000</pubDate>
      <link>https://dev.to/ajipelumi/i-tried-to-catch-nextjs-contradicting-itself-it-never-did-3e63</link>
      <guid>https://dev.to/ajipelumi/i-tried-to-catch-nextjs-contradicting-itself-it-never-did-3e63</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path One: Ship an Agent That Queries Real Content&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Next.js has two routers. The Pages Router and the App Router answer the same questions in incompatible ways, and both sets of documentation are live, indexed, and correct. Neither page carries a banner saying "this is the old way." Search does not tell you which one you landed on.&lt;/p&gt;

&lt;p&gt;So an AI assistant or an old tutorial hands you &lt;code&gt;getServerSideProps&lt;/code&gt;, you paste it into an App Router project, and Next.js rejects it. The answer was not wrong. It was for the other router.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Router Rosetta&lt;/strong&gt; takes a snippet or a question, identifies which router it belongs to, and gives you the equivalent in the other one. Every claim carries the documentation entry it came from, and every equivalence carries a citation to the official migration guide.&lt;/p&gt;

&lt;p&gt;It also refuses. If the knowledge base does not cover something, it says so rather than filling the gap from the model's memory of Next.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://router-rosetta.vercel.app" rel="noopener noreferrer"&gt;https://router-rosetta.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paste code, or use one of the four examples on the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  It translates, with receipts
&lt;/h3&gt;

&lt;p&gt;Given this:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getServerSideProps&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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/posts&lt;/span&gt;&lt;span class="dl"&gt;'&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&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;json&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;It answers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This code uses &lt;strong&gt;Pages Router&lt;/strong&gt;, as identified by the use of &lt;code&gt;getServerSideProps&lt;/code&gt; [data_fetching/pages_static].&lt;/p&gt;

&lt;p&gt;In the App Router, &lt;code&gt;getServerSideProps&lt;/code&gt; is replaced by fetching data directly inside an &lt;code&gt;async&lt;/code&gt; Server Component page [migration, data_fetching/app_router]. In the App Router, &lt;code&gt;fetch&lt;/code&gt; calls inside Server Components run on the server during rendering [data_fetching/app_router], mapping to &lt;code&gt;getServerSideProps&lt;/code&gt; dynamic request behavior [migration].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then it writes the App Router version, citing the target entry and the migration&lt;br&gt;
guide for the equivalence.&lt;/p&gt;
&lt;h3&gt;
  
  
  It refuses, with partial credit
&lt;/h3&gt;

&lt;p&gt;Ask it something out of scope:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do I configure Redis session storage with a custom Kubernetes ingress in Next.js?&lt;/p&gt;

&lt;p&gt;The knowledge base does not contain information on configuring Redis session storage or custom Kubernetes ingress routing in Next.js.&lt;/p&gt;

&lt;p&gt;The only Kubernetes and deployment details supported by the knowledge base are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js supports deployment to Docker-compatible providers, including Kubernetes, using either a Node.js server or Docker container build [setup/configuration].&lt;/li&gt;
&lt;li&gt;For Docker and containerized environments like Kubernetes, Next.js provides an &lt;code&gt;output: "standalone"&lt;/code&gt; configuration mode in &lt;code&gt;next.config.js&lt;/code&gt; to create a minimal production image containing only the required runtime files [setup/configuration].&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Redis session storage and custom Kubernetes ingress configurations are not covered in the knowledge base, no router-specific mapping or implementation code can be provided.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ajipelumi" rel="noopener noreferrer"&gt;
        ajipelumi
      &lt;/a&gt; / &lt;a href="https://github.com/ajipelumi/router-rosetta" rel="noopener noreferrer"&gt;
        router-rosetta
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Router Rosetta&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Translates Next.js code and questions between the &lt;strong&gt;Pages Router&lt;/strong&gt; and the &lt;strong&gt;App Router&lt;/strong&gt;, citing a knowledge base entry for every claim.&lt;/p&gt;

&lt;p&gt;Paste &lt;code&gt;getServerSideProps&lt;/code&gt; and it tells you this is Pages Router code, gives you the async Server Component equivalent, and cites the docs entry behind each statement. Ask "how do I read search params in each router?" and it answers for both. Every factual claim carries a citation chip — the point is that you can check the answer rather than trust it.&lt;/p&gt;

&lt;p&gt;The model is instructed not to answer from its own memory of Next.js. If the knowledge base does not cover a mapping, it says so instead of guessing.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting started&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install
cp .env.local.example .env.local   &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; then fill in the values below&lt;/span&gt;
npm run dev&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="nofollow noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Environment&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;The two Sanity variables are required. At least one model provider key is also
required…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/ajipelumi/router-rosetta" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Used Sanity
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What I pointed Context at
&lt;/h3&gt;

&lt;p&gt;Three website sources, all on &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;nextjs.org&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Pages&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/docs/pages/building-your-application&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;The superseded way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/docs/app/getting-started&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;The current way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/docs/app/guides/migrating/app-router-migration&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;The arbiter&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;42 pages, no dataset or uploaded files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One setting mattered more than everything else: Max depth.&lt;/strong&gt; Left empty, the crawler reads the seed URL and follows nothing. My first build pulled 7 pages out of a 38 page target and I spent a while blaming my include patterns. Setting Max depth to 3 took it to 42.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the build produced
&lt;/h3&gt;

&lt;p&gt;Context turned those 42 pages into 16 entries, and it organised them into &lt;strong&gt;pairs&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Pages Router&lt;/th&gt;
&lt;th&gt;App Router&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data fetching&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data_fetching/pages_static&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;data_fetching/app_router&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Routing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;routing/pages_router&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;routing/app_router&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rendering&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rendering/pages_router_modes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;caching&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plus &lt;code&gt;migration&lt;/code&gt;, holding the deprecated-to-current API mapping.&lt;/p&gt;

&lt;p&gt;Each entry also carries an &lt;code&gt;excludes:&lt;/code&gt; line pointing at its counterpart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data_fetching/app_router [peripheral]
  App Router data fetching: Server Components with fetch API or ORM/database...
  excludes: Pages Router methods (getStaticProps, getStaticPaths,
            getServerSideProps); cache/revalidation config.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line is the whole product: the knowledge base records where the other half of each answer lives, and I did not model that pairing myself. The build wrote it from the source material.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which tools the agent uses
&lt;/h3&gt;

&lt;p&gt;The endpoint serves Knowledge Base mode, so two tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;initial_context&lt;/code&gt; for the outline, which is small enough to hold for a whole conversation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;knowledge_base_read&lt;/code&gt; for the entries themselves, several paths at a time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system prompt forces the pairing to be used rather than hoped for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read the entry for that router AND its paired entry for the other router. Read&lt;br&gt;
the migration entry whenever you assert an equivalence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without that line, the model answers from whichever entry it reads first and produces a confident, one-sided answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this needs structured content
&lt;/h3&gt;

&lt;p&gt;Keyword search for &lt;code&gt;getServerSideProps&lt;/code&gt; returns the Pages Router documentation. It is the right page, current and accurate, and it will not tell you that a newer router replaced it or what the replacement looks like. The answer requires knowing which of two parallel systems a claim belongs to and what it maps onto, a relation between sources rather than a string inside any one of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  What did not work
&lt;/h3&gt;

&lt;p&gt;I started by trying to surface &lt;strong&gt;contradictions&lt;/strong&gt; between the two routers, because the Sanity docs describe conflict detection and it sounded like a strong demo.&lt;/p&gt;

&lt;p&gt;The first build, the starved one with 7 pages, raised a real conflict about &lt;code&gt;getStaticPaths&lt;/code&gt; &lt;code&gt;fallback&lt;/code&gt; values.&lt;/p&gt;

&lt;p&gt;Then I fixed the crawl, rebuilt with all 42 pages, and got &lt;strong&gt;zero conflicts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That result is correct. With the full picture, including the migration guide explaining that one router supersedes the other, Context concluded these are two coherent systems rather than one system contradicting itself. More context produced better reconciliation. The conflict I had been excited about was an artifact of an incomplete index.&lt;/p&gt;

&lt;p&gt;Next.js does not contradict itself, it documents two things in parallel, and that only became visible once the crawl was complete. I kept the same 42 pages and the same paired structure, and changed the product from adjudication to&lt;br&gt;
translation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build notes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Next.js 16, Vercel AI SDK v7, &lt;code&gt;@ai-sdk/mcp&lt;/code&gt; connecting to Context MCP, Groq as the model with Gemini as a fallback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agent is provider-agnostic, and that is the point.&lt;/strong&gt; All the domain knowledge lives in the knowledge base rather than in the model. Neither provider knows anything about Next.js routing beyond what it reads through MCP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free tiers forced real engineering, not just a config swap.&lt;/strong&gt; Gemini's free tier caps at 20 requests per day per model, which a demo exhausts in minutes, so Groq is the primary and Gemini is the fallback. Three things make that survivable in production: the client mirrors the limit with a sliding window so you aretold before a request is wasted, first-turn answers are cached for a day so the example buttons do not re-derive the same output, and a failed request actually switches provider mid-run rather than just erroring, using the AI SDK's &lt;code&gt;prepareStep&lt;/code&gt; so the swap sticks for the rest of the agent's steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Errors were invisible until I made them loud.&lt;/strong&gt; The AI SDK's default failure message is "An error occurred," identical for every cause. Wiring &lt;code&gt;onError&lt;/code&gt; to report the real cause was one of the first things worth doing, well before therate limits above ever came up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Project ID:&lt;/strong&gt; &lt;code&gt;3bpjqog6&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Dataset:&lt;/strong&gt; &lt;code&gt;production&lt;/code&gt; (public)&lt;br&gt;
&lt;strong&gt;Knowledge Base ID:&lt;/strong&gt; &lt;code&gt;kbm133o2qNEI&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The project is deliberately empty. The knowledge base is built entirely from website sources, so no dataset content was needed. The structure worth looking at is in the Knowledge Base entries, not in the Content Lake.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
    <item>
      <title>The Metric You Report Is Not the Work You Did</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Fri, 18 Sep 2026 14:51:13 +0000</pubDate>
      <link>https://dev.to/ajipelumi/the-metric-you-report-is-not-the-work-you-did-29da</link>
      <guid>https://dev.to/ajipelumi/the-metric-you-report-is-not-the-work-you-did-29da</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;North Star Metric&lt;/strong&gt; is one number that represents value delivered. That is the whole definition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The difficulty is never understanding it. The difficulty is that when you pick one honestly for something you actually do, it is almost never the number you have been keeping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it on a job search
&lt;/h2&gt;

&lt;p&gt;Suppose you are looking for work, and you want one number to tell you whether the search is going well.&lt;/p&gt;

&lt;p&gt;The first candidate that comes to mind is applications sent. It is easy to count, it goes up every week, and it feels like effort. Most people searching for a job are tracking it whether they call it a metric or not.&lt;/p&gt;

&lt;p&gt;It is also close to useless. Fifty applications sent tells you nothing about whether any of them were read. It is perfectly possible to send fifty and be no closer to a job than when you started, and the number will still look like a productive month.&lt;/p&gt;

&lt;p&gt;So try the next one: hours spent job searching. This is worse. It measures input rather than output, and it rewards inefficiency directly. Spending six hours rewriting a cover letter for a role you were never going to get scores higher than spending twenty minutes on one you were.&lt;/p&gt;

&lt;p&gt;Try again: responses received. Better, because it requires someone on the other side to have done something. But it still counts rejections, and a rejection is not value delivered to anyone.&lt;/p&gt;

&lt;p&gt;What you are actually after is something like &lt;strong&gt;interviews earned from roles you would genuinely accept&lt;/strong&gt;. That number only moves when a real employer, for a job you actually want, has decided you are worth their time.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Candidate metric&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;th&gt;Why it fails&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Applications sent&lt;/td&gt;
&lt;td&gt;Effort&lt;/td&gt;
&lt;td&gt;Says nothing about whether anyone read them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hours spent searching&lt;/td&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;Rewards inefficiency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Responses received&lt;/td&gt;
&lt;td&gt;Someone reacted&lt;/td&gt;
&lt;td&gt;Counts rejections too&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interviews earned (roles you'd accept)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Value delivered&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice what happened across those four attempts. Each one moved further from what you did and closer to what resulted. That direction of travel is the whole idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the wrong metric is so tempting
&lt;/h2&gt;

&lt;p&gt;Activity is easy to count. Applications sent is a number you already have. Interviews earned requires you to define what counts as a role you would accept, then wait, then attribute the outcome to something you did weeks ago.&lt;/p&gt;

&lt;p&gt;Activity also rises reliably. Send ten applications, the number goes up by ten. Outcome metrics sit still for long stretches and then move in steps, and a flat week on a number you care about feels much worse than a rising week on a number you do not.&lt;/p&gt;

&lt;p&gt;That asymmetry is the reason people pick badly, and it has nothing to do with not understanding the concept. Choosing the honest metric means accepting a number that will often refuse to move while you are working hard. Most people, reasonably, would rather watch the one that rewards them daily.&lt;/p&gt;

&lt;p&gt;The same trade shows up at work. Tickets closed, commits pushed, messages answered, items reviewed. All real work. None of them say whether anything was better afterwards. Compare what each one could be instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tickets closed → problems that stayed fixed&lt;/li&gt;
&lt;li&gt;Commits pushed → features users actually used&lt;/li&gt;
&lt;li&gt;Items reviewed → items usable afterwards&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One number is not enough
&lt;/h2&gt;

&lt;p&gt;A North Star on its own is gameable, usually without anyone intending to game it.&lt;/p&gt;

&lt;p&gt;Optimise interviews earned and the obvious move is to apply only to roles where your odds are highest, which over a few months quietly narrows your search to jobs slightly below what you could get. The metric rises. The search gets worse.&lt;/p&gt;

&lt;p&gt;So it needs a &lt;strong&gt;counterweight&lt;/strong&gt; (often called a &lt;em&gt;guardrail metric&lt;/em&gt;), and one is normally enough. Here it might be the share of applications going to roles that would be a genuine step up. That number stops you buying interviews with ambition.&lt;/p&gt;

&lt;p&gt;The pairing is the point.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One metric tells you how to grow, the other tells you what growth is not allowed to cost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Counting your own work honestly
&lt;/h2&gt;

&lt;p&gt;Once you have tried to pick an honest metric for one mundane thing, it becomes hard not to do it for everything else.&lt;/p&gt;

&lt;p&gt;It changes what you report about your own work most of all. It is easy to say you reviewed two hundred items last week, and much harder to say how many were usable afterwards. The first number is always available and always flattering. The second one is the one that was worth doing.&lt;/p&gt;

&lt;p&gt;The question that gets you there is short:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If this number goes up and nothing else changes, has anything actually improved?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is no, you are counting activity.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>career</category>
    </item>
    <item>
      <title>From Aiven to Railway: Our Database Migration</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Tue, 28 Apr 2026 12:08:53 +0000</pubDate>
      <link>https://dev.to/ajipelumi/from-aiven-to-railway-our-database-migration-2212</link>
      <guid>https://dev.to/ajipelumi/from-aiven-to-railway-our-database-migration-2212</guid>
      <description>&lt;h2&gt;
  
  
  I didn't plan a migration. I just noticed we were paying for two databases.
&lt;/h2&gt;

&lt;p&gt;This whole thing started with a very unglamorous realization:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We were paying for Aiven Postgres…&lt;br&gt;
and also paying for Railway.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At some point I opened the Railway dashboard properly and thought:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Wait — why aren't we using this for the database too?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No incident. No outage. No scaling crisis.&lt;br&gt;&lt;br&gt;
Just mild financial confusion.&lt;/p&gt;




&lt;h2&gt;
  
  
  The decision wasn't really "should we migrate?"
&lt;/h2&gt;

&lt;p&gt;It was more like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We're already using Railway for everything else. Why is the database emotionally living somewhere else?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I did what any developer does when something feels inconsistent: I turned it into a &lt;em&gt;migration project&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The migration itself was weirdly easy
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;scripts&lt;/li&gt;
&lt;li&gt;downtime planning&lt;/li&gt;
&lt;li&gt;CSV exports&lt;/li&gt;
&lt;li&gt;at least one regretful late-night rollback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, Railway just pointed me to their Postgres migrator:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://railway.com/deploy/postgres-migrator" rel="noopener noreferrer"&gt;Railway Postgres Migrator&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The setup was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source:&lt;/strong&gt; Aiven Postgres URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination:&lt;/strong&gt; Railway Postgres&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; Deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. I didn't trust it at first.&lt;/p&gt;




&lt;h2&gt;
  
  
  The one thing that actually tripped me up
&lt;/h2&gt;

&lt;p&gt;Before the migrator worked, I hit a version mismatch. Railway's Postgres was running v18 — and the migrator requires source and destination to be on the same major version. Aiven was v17, so I had to upgrade it first before anything else would proceed.&lt;/p&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%2F7ryfmti4tzp0ftd9ye4o.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%2F7ryfmti4tzp0ftd9ye4o.png" alt="A screenshot of a PostgreSQL migration log showing timestamped entries from April 27, 2026. It records validation steps for source and target databases, indicating the source is PostgreSQL 17.9 and the target is PostgreSQL 18.3. The final line displays an error message stating “connection validation failed: major version mismatch,” highlighting that both databases must use the same major version." width="799" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once versions matched, there was another quiet gotcha: two optional environment variables — &lt;code&gt;NO_ACL&lt;/code&gt; and &lt;code&gt;NO_OWNER&lt;/code&gt; — had to be set to &lt;code&gt;true&lt;/code&gt; for the migration to complete cleanly.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NO_ACL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skips restoring access privileges (GRANT/REVOKE commands)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NO_OWNER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skips restoring object ownership (ALTER OWNER commands)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Railway docs show their default as &lt;code&gt;true&lt;/code&gt;, but in practice they were &lt;code&gt;false&lt;/code&gt; — meaning the migrator was trying to restore ACLs and ownership from Aiven, hitting permission errors, and failing silently enough that it wasn't immediately obvious why.&lt;/p&gt;

&lt;p&gt;Setting both to &lt;code&gt;true&lt;/code&gt; explicitly is what actually made it work.&lt;/p&gt;




&lt;h2&gt;
  
  
  I kept waiting for something to break. Nothing did.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No data loss.
&lt;/li&gt;
&lt;li&gt;No partial sync weirdness.
&lt;/li&gt;
&lt;li&gt;No "why is production dead" moment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just a working database in a new place, which is almost more stressful than failure — because you keep checking if you missed something.&lt;/p&gt;




&lt;h2&gt;
  
  
  Railway vs Aiven (honest experience, not a benchmark)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Aiven Postgres:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solid and reliable&lt;/li&gt;
&lt;li&gt;More manual setup&lt;/li&gt;
&lt;li&gt;External to your deployment ecosystem&lt;/li&gt;
&lt;li&gt;SSL and config overhead you have to manage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Railway Postgres:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lives inside your deployment environment&lt;/li&gt;
&lt;li&gt;Internal networking by default&lt;/li&gt;
&lt;li&gt;Less cognitive overhead on config&lt;/li&gt;
&lt;li&gt;Everything in one dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest difference wasn't performance. It was &lt;strong&gt;mental overhead&lt;/strong&gt;. Fewer places to check. Fewer credentials to rotate. One less tab permanently open.&lt;/p&gt;




&lt;h2&gt;
  
  
  Would I recommend this?
&lt;/h2&gt;

&lt;p&gt;If you're already on Railway and running an external Postgres for no strong reason — probably yes. The migrator genuinely works, the internal networking is a real quality-of-life upgrade, and the consolidation is worth it just to stop context-switching between dashboards.&lt;/p&gt;

&lt;p&gt;If you have a strong reason for keeping the database external (compliance, specific Aiven features, multi-cloud setups) — that calculus changes. But "we started there and never questioned it" isn't a strong reason.&lt;/p&gt;

&lt;p&gt;Sometimes the migration is just noticing you forgot to consolidate.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>postgres</category>
      <category>productivity</category>
      <category>startup</category>
    </item>
    <item>
      <title>Understanding Value and Pointer Receivers in Go: Building a Crypto Tracker</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Thu, 06 Nov 2025 00:56:05 +0000</pubDate>
      <link>https://dev.to/ajipelumi/understanding-value-and-pointer-receivers-in-go-building-a-crypto-tracker-lkn</link>
      <guid>https://dev.to/ajipelumi/understanding-value-and-pointer-receivers-in-go-building-a-crypto-tracker-lkn</guid>
      <description>&lt;p&gt;I picked up Go recently while building the backend of a crypto tracker. This tracker allows users to manage their cryptocurrency holdings and calculate their wallet value in real-time. One of my favorite learnings so far is value receivers and pointer receivers, and how everything works under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Go Passes Arguments
&lt;/h2&gt;

&lt;p&gt;Go passes arguments by value, not by reference. This means it creates a copy of the argument and passes that copy to the function. Understanding this is crucial to writing efficient Go code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Value Receivers: Working with Copies
&lt;/h3&gt;

&lt;p&gt;When you use a value receiver, Go creates a copy of the struct. Any modifications you make inside the function only affect the copy, not the original.&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;type&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;User&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;TotalValue&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Value receiver - receives a copy&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;UpdateValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;  &lt;span class="c"&gt;// Only modifies the copy&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;"Inside function:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&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="n"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"pelumi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TotalValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdateValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5000.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;"Outside function:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&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;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Inside &lt;span class="k"&gt;function&lt;/span&gt;: 5000
Outside &lt;span class="k"&gt;function&lt;/span&gt;: 1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original &lt;code&gt;wallet.TotalValue&lt;/code&gt; remains 1000 because we modified a copy. When the function returns, the copy is discarded (removed from the stack), and our changes are lost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointer Receivers: Modifying the Original
&lt;/h3&gt;

&lt;p&gt;Pointer receivers solve this by passing the memory address of the struct instead of a copy. When you pass an address, Go still passes it by value, but now you have an address that points to the original data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Pointer receiver - receives a pointer (address)&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;UpdateValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt;  &lt;span class="c"&gt;// Modifies the original&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;"Inside function:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&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="n"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"pelumi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TotalValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdateValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// Go automatically passes &amp;amp;wallet&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;"Outside function:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&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;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Inside &lt;span class="k"&gt;function&lt;/span&gt;: 5000
Outside &lt;span class="k"&gt;function&lt;/span&gt;: 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now both print 5000 because we modified the original through its address. Go automatically handles the dereferencing (you don't need to write &lt;code&gt;(*w).TotalValue&lt;/code&gt;), making the syntax clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example: My Crypto Tracker
&lt;/h2&gt;

&lt;p&gt;In my crypto tracker, I use pointer receivers when updating wallet data:&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;type&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt;         &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;User&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;TotalValue&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;
    &lt;span class="n"&gt;Holdings&lt;/span&gt;   &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Holding&lt;/span&gt;
    &lt;span class="n"&gt;UpdatedAt&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;Time&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Pointer receiver because we need to modify the wallet&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;CalculateTotalValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priceService&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PriceService&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="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0.0&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;holding&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;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Holdings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;price&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;priceService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FetchCoinPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;holding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coin&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;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;holding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;      &lt;span class="c"&gt;// Modifies the original wallet&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedAt&lt;/span&gt; &lt;span class="o"&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="c"&gt;// Updates timestamp&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I used a value receiver here, the calculated &lt;code&gt;TotalValue&lt;/code&gt; and &lt;code&gt;UpdatedAt&lt;/code&gt; would never be saved to the original wallet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack vs Heap Trade-off
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. Value receivers are faster because they use the stack, but pointer receivers can be slower because they may require the heap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack (Fast)
&lt;/h3&gt;

&lt;p&gt;When you use value receivers, Go allocates the copy on the stack. The stack is fast because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory is automatically managed (allocated and freed when functions return)&lt;/li&gt;
&lt;li&gt;Memory is local and cache-friendly&lt;/li&gt;
&lt;li&gt;No garbage collector involvement
&lt;/li&gt;
&lt;/ul&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUser&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;  &lt;span class="c"&gt;// Fast: uses stack memory&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Heap (Slower, but Necessary)
&lt;/h3&gt;

&lt;p&gt;When you use pointer receivers, Go might allocate the variable on the heap if it "escapes" the function scope. This happens because the address could be referenced later in the program, so Go can't safely discard it when the function returns.&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;UpdateAndReturn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;  &lt;span class="c"&gt;// Address escapes, so w lives on the heap&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The heap is slower because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The garbage collector must track and clean up these allocations&lt;/li&gt;
&lt;li&gt;Memory access is less cache-friendly&lt;/li&gt;
&lt;li&gt;Allocation and deallocation take more time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, this trade-off is usually worth it when you need to modify large structs or avoid expensive copies.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Each
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use Value Receivers When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You don't need to modify the struct&lt;/li&gt;
&lt;li&gt;The struct is small (a few fields)&lt;/li&gt;
&lt;li&gt;You want maximum performance for read-only operations
&lt;/li&gt;
&lt;/ul&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;IsEmpty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Holdings&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="c"&gt;// Just reading, no modifications&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUserInfo&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="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;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User: %s, Value: $%.2f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalValue&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;
  
  
  Use Pointer Receivers When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need to modify the struct&lt;/li&gt;
&lt;li&gt;The struct is large (copying would be expensive)&lt;/li&gt;
&lt;li&gt;You want consistency (if some methods use pointers, use them everywhere)
&lt;/li&gt;
&lt;/ul&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;AddHolding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;holding&lt;/span&gt; &lt;span class="n"&gt;Holding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Holdings&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;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Holdings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;holding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;// Modifies original&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ClearHoldings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Holdings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Holding&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;  &lt;span class="c"&gt;// Modifies original&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A Common Pattern: Mixing Both
&lt;/h3&gt;

&lt;p&gt;In practice, if you have any pointer receiver methods, it's best to use pointer receivers for all methods on that type for consistency:&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;type&lt;/span&gt; &lt;span class="n"&gt;Wallet&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;User&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;TotalValue&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Even though this doesn't modify, use pointer for consistency&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;GetUser&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// This modifies, so it needs a pointer&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;SetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I've Learned
&lt;/h2&gt;

&lt;p&gt;Coming from C, I understood pointers conceptually, but Go's implementation adds an interesting layer. The automatic dereferencing makes pointer receivers feel natural to use, and the compiler's escape analysis intelligently decides when to use the heap vs stack.&lt;/p&gt;

&lt;p&gt;The key insight: value receivers are best when you don't intend to modify the argument, while pointer receivers are perfect when you want to modify a passed argument or when you want to avoid copying large values.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>go</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Virtual Environments: My "Aha!" Moment</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Tue, 25 Feb 2025 14:34:43 +0000</pubDate>
      <link>https://dev.to/ajipelumi/virtual-environments-my-aha-moment-3hi7</link>
      <guid>https://dev.to/ajipelumi/virtual-environments-my-aha-moment-3hi7</guid>
      <description>&lt;p&gt;This is going to seem weird, but I have seen "virtual environments" flying around since I started coding in 2022. Yet, I only just &lt;em&gt;got&lt;/em&gt; the hang of them. For the longest time, they looked like some overly complex sorcery, and I would shy away every time someone mentioned them.&lt;/p&gt;

&lt;p&gt;Fast forward to now, I am sitting here wondering how I have been surviving without them all this time.&lt;/p&gt;

&lt;p&gt;Do not get me wrong, I had used virtual environments before. I knew how to spin one up when following tutorials, but it was always just one of those "do this because they said so" moments. I never really understood the power they wielded. But recently? Oh boy, things changed! I finally hacked how powerful they can be when properly integrated into a project, and now I cannot imagine coding without them.&lt;/p&gt;

&lt;p&gt;So, here is how I have been rolling with virtual environments lately.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Even Is a Virtual Environment?
&lt;/h3&gt;

&lt;p&gt;If you are like how I used to be, you might have nodded along when people talked about virtual environments without actually knowing what they were. A virtual environment is basically a self-contained workspace where you can install project-specific dependencies without messing up your system-wide Python setup.&lt;/p&gt;

&lt;p&gt;Think of it like a bubble for your project, everything it needs stays inside, away from global Python, keeping things clean and conflict-free. No more worrying about breaking other projects because some library decided to update itself to an incompatible version.&lt;/p&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%2Fqkwyxpwothebfr2rh0vb.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%2Fqkwyxpwothebfr2rh0vb.png" alt="A meme using the " width="651" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How To Setup Virtual Environments
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Create a virtual environment
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv my_env 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a new directory &lt;code&gt;my_env&lt;/code&gt;, which holds all the virtual environment goodies.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Activate it
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On Windows:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  my_env&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On macOS/Linux:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nb"&gt;source &lt;/span&gt;my_env/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once activated, your terminal will show &lt;code&gt;(my_env)&lt;/code&gt;, meaning you are now inside the isolated environment.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Install Dependencies
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;Boom! These packages are now installed only inside &lt;code&gt;my_env&lt;/code&gt;, not system-wide.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Deactivate When Done
&lt;/h4&gt;



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

&lt;/div&gt;



&lt;p&gt;This takes you back to normal, global Python.&lt;/p&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%2Ffpibx2k4i1thysz15kw4.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%2Ffpibx2k4i1thysz15kw4.png" alt="A meme with the caption " start="" width="800" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why You Should Use Virtual Environments
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No More "It Works on My Machine" Nightmares&lt;/strong&gt; - With a virtual environment, dependencies are locked to specific versions, so no more "why does it work for you but not for me?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy Project Switching&lt;/strong&gt; - One can now hop between projects without dependency conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Collaboration&lt;/strong&gt; - When sharing code, others can recreate exact environment using a &lt;code&gt;requirements.txt&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I have finally embraced virtual environments, I cannot believe I was coding without them for so long. If you are like I was, seeing people talk about them but never really using them, trust me, it is worth the 5 minutes to set up. Your projects (and future self) will thank you!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How Dictionary Lookup Operations Are O(1)</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Thu, 05 Sep 2024 18:16:38 +0000</pubDate>
      <link>https://dev.to/ajipelumi/how-dictionary-lookup-operations-are-o1-49pk</link>
      <guid>https://dev.to/ajipelumi/how-dictionary-lookup-operations-are-o1-49pk</guid>
      <description>&lt;p&gt;Imagine you have a magical book that lets you look up any word instantly. You don’t need to flip through pages or search for the right letter section. You just say the word, and &lt;em&gt;poof&lt;/em&gt;, the meaning appears immediately.&lt;/p&gt;

&lt;p&gt;In programming, we have something very similar to this magical book: &lt;strong&gt;dictionaries&lt;/strong&gt; (also called &lt;strong&gt;hashmaps&lt;/strong&gt;). They are special data structures where you can store things in a way that makes finding them super fast, no matter how much you’ve stored.&lt;/p&gt;

&lt;p&gt;But how does this work?&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s a Dictionary in Programming?
&lt;/h2&gt;

&lt;p&gt;A dictionary in programming is like a real-life dictionary, but instead of words and meanings, it stores keys and values. You give it a key (something unique like a number, a name, or a word), and it will give you back its value (like a description, number, or some other information related to the key).&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;fruit_colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Apple&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Red&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Yellow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Grapes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Purple&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Key: "Apple"&lt;/li&gt;
&lt;li&gt;Value: "Red"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Magic Behind O(1) Lookup
&lt;/h2&gt;

&lt;p&gt;So, how does the dictionary give you answers so quickly? The secret is something called &lt;strong&gt;hashing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s think about it this way: if you had a huge pile of toys and someone said, "Find me the red car" you would have to look through the whole pile one by one, that would take time, especially if there are lots of toys.&lt;/p&gt;

&lt;p&gt;Now, imagine if every toy had a magical sticker with an exact spot where it should go on a shelf. Every time you put a toy away, it goes straight to its specific spot. So, if you wanted the red car, you would know exactly where it is without looking through everything.&lt;/p&gt;

&lt;p&gt;This is exactly how a &lt;strong&gt;hashmap&lt;/strong&gt; works. When you add something to a dictionary, a special function (called a hash function) creates a magical "sticker" (called a &lt;strong&gt;hash&lt;/strong&gt;), which tells the dictionary where to store the value.&lt;/p&gt;

&lt;p&gt;When you ask for that value later using its key, the dictionary uses the same "sticker" to go straight to the spot and grab the value in one step. This quick access, no matter how much stuff you have, is called &lt;strong&gt;constant time&lt;/strong&gt; or &lt;strong&gt;O(1)&lt;/strong&gt; in Big O notation. It means the lookup time doesn’t grow even if you have millions of items stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking If a Key Exists in a Dictionary
&lt;/h2&gt;

&lt;p&gt;When working with dictionaries, one common task is checking if a specific key exists. For example, in our magical toy shelf, one might want to know if a red car is already on the shelf before adding another one.&lt;/p&gt;

&lt;p&gt;There are two ways you might write this check in Python:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if key in dictionary&lt;/li&gt;
&lt;li&gt;if key in dictionary.keys()&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first glance, both seem to do the same thing however, there is a big difference in efficiency, and here is why.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;if key in dictionary&lt;/code&gt; – The Fast, Direct Way&lt;br&gt;
When you write &lt;code&gt;if key in dictionary&lt;/code&gt;, Python automatically looks for the key in the dictionary’s "shelf" of keys. It knows exactly where to check, thanks to the hashing system we talked about earlier. This lookup is done in O(1) time, meaning it is super fast.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;if key in dictionary.keys()&lt;/code&gt; – The Slower, Extra Work Way&lt;br&gt;
On the other hand, if you write &lt;code&gt;if key in dictionary.keys()&lt;/code&gt;, you are explicitly asking Python to first pull out all the keys from the dictionary and store them in a special "view" object. Then, Python checks if the key is in that view. While this still works, it is slower because you are adding an extra step: creating the view of all the keys.&lt;/p&gt;

&lt;p&gt;Always use &lt;code&gt;if key in dictionary&lt;/code&gt;. It is the faster, more efficient way to check if a key exists in a dictionary.&lt;/p&gt;

&lt;p&gt;Dictionaries in programming are powerful tools, allowing you to store and retrieve data quickly, thanks to the magic of hashing. With constant-time lookups (O(1)), they enable you to efficiently manage large amounts of data without worrying about performance issues. Whether you are checking if a key exists or retrieving a value, dictionaries are your go-to structure for fast and effective operations.&lt;/p&gt;

&lt;p&gt;Have any experiences with using dictionaries? Share your thoughts and tips in the comments below!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Goroutines and sync.WaitGroup in Golang</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Thu, 11 Jan 2024 16:13:37 +0000</pubDate>
      <link>https://dev.to/ajipelumi/goroutines-and-syncwaitgroup-in-golang-11dk</link>
      <guid>https://dev.to/ajipelumi/goroutines-and-syncwaitgroup-in-golang-11dk</guid>
      <description>&lt;p&gt;Creating a booking app with Golang has been a thrilling experience, filled with challenges and chances to learn. I came across something interesting while working on managing Goroutines using &lt;code&gt;sync.WaitGroup&lt;/code&gt;. It reminded me of the asynchronous and await functionality in Node.js, providing a strong way to handle multiple tasks at the same time in Golang. However, when I initially tried to add &lt;code&gt;sync.WaitGroup&lt;/code&gt; to my code, I encountered an unexpected error, I found out that it was crucial to create an instance of &lt;code&gt;sync.WaitGroup&lt;/code&gt; for it to work smoothly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Importance of Goroutines
&lt;/h2&gt;

&lt;p&gt;Goroutines are a key feature in Golang that allow functions to run concurrently. They are lightweight, making them great for handling many tasks simultaneously without affecting performance. To make Goroutines even more efficient, I explored using &lt;code&gt;sync.WaitGroup&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Sync.WaitGroup
&lt;/h2&gt;

&lt;p&gt;In Golang, &lt;code&gt;sync.WaitGroup&lt;/code&gt; is designed to coordinate Goroutines. It keeps track of the number of active Goroutines, making it useful in situations where you want to make sure all concurrent tasks are completed before moving on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Learning Experience
&lt;/h2&gt;

&lt;p&gt;While trying to add &lt;code&gt;sync.WaitGroup&lt;/code&gt; to my booking app, I faced an error that made me realize that creating an instance of &lt;code&gt;sync.WaitGroup&lt;/code&gt; is necessary before using its methods. The three main methods - &lt;code&gt;Add()&lt;/code&gt;, &lt;code&gt;Wait()&lt;/code&gt;, and &lt;code&gt;Done()&lt;/code&gt; - are crucial for synchronization. If we try to call &lt;code&gt;sync.WaitGroup.Add(1)&lt;/code&gt;, the Go compiler will give an error because &lt;strong&gt;Add&lt;/strong&gt; is not a function of the &lt;code&gt;sync.WaitGroup&lt;/code&gt; type that can be called directly. It's a method that needs an instance of &lt;code&gt;sync.WaitGroup&lt;/code&gt; to be called upon.&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;"sync"&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;// Create an instance of sync.WaitGroup&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;

    &lt;span class="c"&gt;// Example Goroutine&lt;/span&gt;
    &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&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="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Your concurrent task logic here&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;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;

    &lt;span class="c"&gt;// Wait for all Goroutines to finish&lt;/span&gt;
    &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&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;By creating an instance of &lt;code&gt;sync.WaitGroup&lt;/code&gt; and using its methods in the right context, I seamlessly integrated it into my booking app. This ensured smooth coordination between Goroutines.&lt;/p&gt;

&lt;p&gt;If you have any stories about using &lt;code&gt;sync.WaitGroup&lt;/code&gt;, feel free to share them in the comments below. I would love to hear about your experiences!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Journey to Becoming a Software Engineer: My Year at ALX</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Fri, 20 Oct 2023 22:58:55 +0000</pubDate>
      <link>https://dev.to/ajipelumi/the-journey-to-becoming-a-software-engineer-my-year-at-alx-p0a</link>
      <guid>https://dev.to/ajipelumi/the-journey-to-becoming-a-software-engineer-my-year-at-alx-p0a</guid>
      <description>&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%2Flq3iior1qqrd02920gd3.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%2Flq3iior1qqrd02920gd3.png" alt="A mail screenshot of my congratulatory message into ALX Software Engineering Program" width="626" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On August 15, 2022, I began a journey that changed my life's course. The &lt;a href="https://www.alxafrica.com/software-engineering-plus/" rel="noopener noreferrer"&gt;ALX Software Engineering&lt;/a&gt; program was my choice. At first, I was both excited and uncertain about what lay ahead. I'd been self-taught, but I longed for structured learning and a community of like-minded individuals. ALX was my answer.&lt;/p&gt;

&lt;p&gt;Today, on October 20, 2023, as I graduate from this transformative program, I'm brimming with excitement and a profound sense of achievement. The past year has been a remarkable journey of self-discovery and growth, one I'm eager to share with you in this post. It's been a time of refining my skills and broadening my horizons, and I can't wait to see where this newfound knowledge will lead me.&lt;/p&gt;

&lt;p&gt;My year at ALX was like an exciting journey where I not only became a better coder but also honed my problem-solving skills. The challenging curriculum and unwavering support from my fellow learners and mentors transformed everyday problems into chances for personal growth. My ability to think critically, debug, and solve complex problems all improved significantly, giving me the confidence to handle challenges with ease.&lt;/p&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%2Fnp27radpxw0cnunezobv.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%2Fnp27radpxw0cnunezobv.png" alt="A software engineering meme" width="659" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yet, one big realization during this year was the strength of community. The program created an atmosphere where working together was key. It wasn't only about standing out individually but also about supporting each other. The feeling of togetherness and sharing knowledge with my peers meant a lot. It taught me that the journey becomes more special when you're alongside others who have the same passion.&lt;/p&gt;

&lt;p&gt;While at ALX, I discovered a knack for explaining complex technical stuff in simple terms. This newfound passion for making the complicated seem easy has become a vital part of my journey. Feel free to check out my articles &lt;a href="https://dev.to/ajipelumi"&gt;here&lt;/a&gt; for a deeper dive into topics that pique my interest.&lt;/p&gt;

&lt;p&gt;As I gaze ahead, I see a world filled with opportunities. In the next 5-10 years, I imagine using my Software Engineering skills to make a positive impact on communities in Africa and beyond. My time at ALX has armed me with not just technical skills, but also the mindset and determination to effect change. I'm thrilled to apply my knowledge to tackle real-world issues and devise solutions that can benefit society.&lt;/p&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%2Fmxx0szfnmb7i881igykv.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%2Fmxx0szfnmb7i881igykv.png" alt="ALX 2023 Graduation graphic" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In summary, my year at ALX Africa has been an exceptional chapter in my software engineering journey. It transformed me, broadening my horizons, honing my skills, and instilling a deep sense of purpose. I found my community, uncovered a passion for technical writing, and gained the confidence to tackle complex challenges. The future holds abundant promise, and I can't wait to seize its opportunities.&lt;/p&gt;

&lt;p&gt;I invite you to dive into my articles here and reach out to &lt;a href="https://www.linkedin.com/in/ajisafeoluwapelumi" rel="noopener noreferrer"&gt;me&lt;/a&gt;. If you're on a similar path or have a story to share, I'd be thrilled to connect with you. Together, let's keep learning, growing, and inspiring one another.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Composition and Inheritance: From Cars to Cars</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Mon, 28 Aug 2023 23:35:44 +0000</pubDate>
      <link>https://dev.to/ajipelumi/composition-and-inheritance-from-cars-to-cars-gld</link>
      <guid>https://dev.to/ajipelumi/composition-and-inheritance-from-cars-to-cars-gld</guid>
      <description>&lt;p&gt;Hello and welcome! A while back, I tackled a real-world problem through Forage, and the company at the center was &lt;a href="https://www.lyft.com/" rel="noopener noreferrer"&gt;Lyft&lt;/a&gt;. While much of the material covered wasn't entirely new, what intrigued me were the concepts of &lt;em&gt;composition&lt;/em&gt; and &lt;em&gt;inheritance&lt;/em&gt;. These concepts played a central role in shaping Lyft's backend infrastructure and I thought to share my newfound knowledge with you. Stay tuned.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composition: Assembling a Car
&lt;/h3&gt;

&lt;p&gt;Let's say we are putting together a car. A car is made up of lots of different parts, and instead of making the whole car all at once, we start by making smaller pieces. Then, we fit these pieces together to build the complete car. This way of building things is called &lt;em&gt;composition&lt;/em&gt;. It's a helpful approach because it gives our car flexibility. We can easily add or remove pieces as needed. This makes our finished car more versatile.&lt;/p&gt;

&lt;p&gt;Let's see an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Wheels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;brand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;brand&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wheels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Wheels&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are defining three classes: &lt;code&gt;Engine&lt;/code&gt;, &lt;code&gt;Wheels&lt;/code&gt;, and &lt;code&gt;Car&lt;/code&gt;. Engine and Wheels represent parts of a car while the Car class brings together an engine and wheels to construct a complete car.&lt;/p&gt;

&lt;p&gt;We see that each part has its own functionality with specific responsibilities. The engine starts, the wheels rotate and the car cars (I thought that would come out right).&lt;/p&gt;

&lt;h3&gt;
  
  
  Inheritance: Family Traits Passed On
&lt;/h3&gt;

&lt;p&gt;Inheritance is in the name, we inherit traits from our parents. Same applies here as our classes inherit characteristics from their base class. We can create new things based on existing things and pass down their characteristics. This approach makes us more efficient as we do not need to create things from the start.&lt;/p&gt;

&lt;p&gt;Let's see an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;brand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;brand&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Vehicle&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="c1"&gt;# Creating an instance of Car
&lt;/span&gt;&lt;span class="n"&gt;my_car&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Toyota&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My car&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s brand is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;my_car&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;brand&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Car&lt;/code&gt; class inherits the &lt;code&gt;brand&lt;/code&gt; attribute from the &lt;code&gt;Vehicle&lt;/code&gt; class without needing to redefine the &lt;code&gt;__init__&lt;/code&gt; method. The Car class inherits all attributes and methods from the Vehicle class while also retaining the ability to redefine and implement its own variations.&lt;/p&gt;

&lt;p&gt;You could use both composition and inheritance together to get the best out of your application. If you'd like to explore how I employed both to address the Lyft challenge, you can find the code &lt;a href="https://github.com/ajipelumi/forage-lyft-starter-repo/tree/main" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you've got stories about using composition and inheritance, I would love to hear them. Drop a comment below and share your experiences!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Exploring the Magic of async/await in JavaScript!</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Thu, 10 Aug 2023 20:20:14 +0000</pubDate>
      <link>https://dev.to/ajipelumi/exploring-the-magic-of-asyncawait-in-javascript-1k3o</link>
      <guid>https://dev.to/ajipelumi/exploring-the-magic-of-asyncawait-in-javascript-1k3o</guid>
      <description>&lt;p&gt;Today, we're going to uncover &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;. In JavaScript, there are some tasks that can take some time, like asking for information from the internet or reading special files. Instead of making the whole computer wait for these tasks to finish, JavaScript allows other tasks to be handled at the same time while it waits for the response from time-taking tasks. Sometimes, we want to wait for the response before starting other tasks, and that's where &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; comes in.&lt;/p&gt;

&lt;h3&gt;
  
  
  async
&lt;/h3&gt;

&lt;p&gt;In simple words, &lt;code&gt;async&lt;/code&gt; is a special word we put in front of a function to tell our computer, "Hey, inside this function, there might be things that take a bit of time to finish."&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Asynchronous operation&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  await
&lt;/h3&gt;

&lt;p&gt;We use the &lt;code&gt;await&lt;/code&gt; keyword inside an &lt;code&gt;async&lt;/code&gt; function and it's a way to tell our computer, "Hey, take a break and pause execution here until this thing is done."&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Pauses until getData() is resolved&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="nx"&gt;result&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;Let's explore the use of &lt;em&gt;async&lt;/em&gt; and &lt;em&gt;await&lt;/em&gt; with some examples.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 1: Without async/await
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Imagine fetching a cat fact from an API using Promises&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://catfact.ninja/fact&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Oops! This won't work as expected&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cat Fact Ninja!&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&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;reject&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="c1"&gt;// Fetch the cat fact from the API&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we have two functions: &lt;em&gt;main()&lt;/em&gt; and &lt;em&gt;fetch(url)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fetch(url)&lt;/code&gt; function takes a URL as input and returns a Promise (a Promise ensures that our request is completed). Inside this Promise, we request a cat fact from the internet.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;main()&lt;/code&gt; function starts by setting a URL for a cat fact API. Then it calls the &lt;code&gt;fetch(url)&lt;/code&gt; function to get the cat fact data. Since &lt;code&gt;fetch(url)&lt;/code&gt; returns data from the internet, it doesn't immediately provide the result, the function takes time. Therefore, when we try to log &lt;code&gt;response.fact&lt;/code&gt;, it won't work correctly. The next console.log() statement and 'Cat Fact Ninja!' will be printed before the &lt;code&gt;fetch(url)&lt;/code&gt; is resolved and this is not what we want. We can fix this in the next example.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 2: With async/await
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Now let's fetch a cat fact using async/await&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://catfact.ninja/fact&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="c1"&gt;// We await the promise to finish&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fact&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// This works perfectly now!&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cat Fact Ninja!&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&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;reject&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="c1"&gt;// Fetch the cat fact from the API&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we've made a slight change by using the &lt;code&gt;async&lt;/code&gt; keyword in the &lt;code&gt;main()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;main()&lt;/code&gt; function is now marked as &lt;code&gt;async&lt;/code&gt;, which means we can use the &lt;code&gt;await&lt;/code&gt; keyword inside it. When &lt;code&gt;await&lt;/code&gt; is used before calling &lt;code&gt;fetch(url)&lt;/code&gt;, it pauses the execution of the &lt;code&gt;main()&lt;/code&gt; function until we get the result we want. This way, the cat fact data is fetched and assigned to the &lt;code&gt;response&lt;/code&gt; variable before moving on. Now, when we log &lt;code&gt;response.fact&lt;/code&gt;, it will correctly show the cat fact fetched from the API because we were waiting for the function to return using &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; help us do cool things with our code, even if those things take time. If you've got thoughts or stories about using them, I'd love to hear them. Drop a comment below and share your experiences, your feedback means a lot!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Navigating One-To-Many Relationship in MySQL</title>
      <dc:creator>Ajisafe Victor Oluwapelumi</dc:creator>
      <pubDate>Thu, 03 Aug 2023 18:20:44 +0000</pubDate>
      <link>https://dev.to/ajipelumi/navigating-one-many-relationship-in-mysql-187o</link>
      <guid>https://dev.to/ajipelumi/navigating-one-many-relationship-in-mysql-187o</guid>
      <description>&lt;p&gt;While developing the backend for &lt;a href="//quiz.pelumi.tech"&gt;Vim Quizzer&lt;/a&gt;,  I encountered a challenge in handling a list of incorrect questions for our question class due to MySQL's lack of direct support for list structures. To address this, I created an Incorrect Question class and established a One-to-Many relationship between the Question class and the Incorrect Question class, successfully implementing a list structure. In this post, I will explain how I achieved that and also shed light on how the One-to-Many relationship works.&lt;/p&gt;

&lt;p&gt;Before we dive into the technicalities, let me give a brief overview of the project that kick started this adventure. &lt;strong&gt;Vim Quizzer&lt;/strong&gt; is an engaging platform designed to help users learn Vim commands through a series of multiple-choice questions that test their knowledge of this command-line text editor, commonly used on Linux machines. If you're eager to put your Vim skills to the test, you can try the quiz &lt;a href="//quiz.pelumi.tech"&gt;here&lt;/a&gt;, the source code is also available &lt;a href="https://github.com/ajipelumi/vim-quizzer" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Challenge
&lt;/h3&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%2Fdgnvyhcy8jcv8xt52vwt.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%2Fdgnvyhcy8jcv8xt52vwt.png" alt="A snapshot of our database structure" width="799" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MySQL serves as the relational database management system, where we store the questions for our web application. As depicted above, our database structure includes a table named &lt;em&gt;questions&lt;/em&gt; with 4 columns: &lt;em&gt;question_id&lt;/em&gt;, &lt;em&gt;question&lt;/em&gt;, &lt;em&gt;correct_answer&lt;/em&gt;, and &lt;em&gt;incorrect_answers&lt;/em&gt;. The &lt;em&gt;question_id&lt;/em&gt; column holds the question id while the &lt;em&gt;question&lt;/em&gt; column accepts a string to store the question itself, the &lt;em&gt;correct_answer&lt;/em&gt; column also takes a string to store the accurate response for the question. On the other hand, the &lt;em&gt;incorrect_answers&lt;/em&gt; column requires a list of strings to store the various incorrect responses for the question.&lt;/p&gt;

&lt;p&gt;However, MySQL lacks built-in support for list structures so we can't have an &lt;em&gt;incorrect_answers&lt;/em&gt; column. To address this, we had to devise an alternative approach, and that's where One-to-Many relationship came into play.&lt;/p&gt;

&lt;h3&gt;
  
  
  One-To-Many Relationship
&lt;/h3&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%2Fpagey5dls4eu83hakb5n.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%2Fpagey5dls4eu83hakb5n.png" alt="One-To-Many Relationship" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As illustrated above, we have established an &lt;em&gt;incorrect answers&lt;/em&gt; table, which comprises the &lt;em&gt;incorrect_answer_id&lt;/em&gt; column, the &lt;em&gt;question_id&lt;/em&gt; column, and the &lt;em&gt;incorrect_answer&lt;/em&gt; itself. It is evident that the &lt;em&gt;question_id&lt;/em&gt; serves as a shared column between the &lt;em&gt;question&lt;/em&gt; table and the &lt;em&gt;incorrect_answer&lt;/em&gt; table. The incorrect answers to the question in our first illustration are now stored accordingly.&lt;/p&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%2F0pyte8skt2vkufomdxrd.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%2F0pyte8skt2vkufomdxrd.png" alt="Incorrect Answers Table" width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, all 3 incorrect answers possess the same &lt;em&gt;question_id&lt;/em&gt;, signifying that they correspond to one particular question in our database. This linkage ensures that each incorrect answer is appropriately associated with its corresponding question.&lt;/p&gt;

&lt;p&gt;To enable the &lt;em&gt;incorrect_answers&lt;/em&gt; attribute to return the list of all incorrect answers associated with a question, we implemented the following code in our Question class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="c1"&gt;# Create list of incorrect answers
&lt;/span&gt; &lt;span class="n"&gt;incorrect_answers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;relationship&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IncorrectAnswer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all, delete-orphan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our implementation, we take advantage of SQLAlchemy's &lt;strong&gt;relationship&lt;/strong&gt; method to establish a seamless link between &lt;em&gt;incorrect_answers&lt;/em&gt; and the Incorrect Answer class. Thanks to the &lt;strong&gt;backref&lt;/strong&gt; parameter, accessing incorrect answers from a given question becomes a breeze, eliminating the need for complex queries or tangled joins. See snippet below.&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="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;question1&lt;span class="o"&gt;)&lt;/span&gt;
&amp;lt;class &lt;span class="s1"&gt;'__main__.Question'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; question1.question
&lt;span class="s1"&gt;'What is the Vim command to save and exit a file?'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; question1.correct_answer
&lt;span class="s1"&gt;':wq'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; question1.incorrect_answers
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;":w"&lt;/span&gt;, &lt;span class="s2"&gt;":q!"&lt;/span&gt;, &lt;span class="s2"&gt;":s"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Furthermore, the &lt;strong&gt;cascade&lt;/strong&gt; parameter works wonders in ensuring data integrity. When we remove a question, all its associated incorrect answers are gracefully deleted as well. This intelligent handling of cascading deletes prevents any orphaned records from cluttering up the database, maintaining a tidy and organized data structure. This solution brings simplicity and efficiency to the management of question and incorrect answer relationship, streamlining the entire process for optimal performance.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, this implementation is at the core of our backend, making it the backbone of our entire application. Witnessing this implementation work in real-time was truly refreshing. The One-To-Many relationship unlocks the true power and practicality in our application, allowing us to seamlessly manage questions and their corresponding incorrect answers with utmost efficiency.&lt;/p&gt;

&lt;p&gt;I hope this explanation provided a clear understanding of the One-To-Many relationship. Feel free to share your thoughts and experiences in the comments. Whether you have explored this concept before or are eager to delve into it, your insights and feedback are appreciated.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>mysql</category>
      <category>programming</category>
      <category>python</category>
    </item>
  </channel>
</rss>
