<?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: Alessandro Annini</title>
    <description>The latest articles on DEV Community by Alessandro Annini (@ale_annini).</description>
    <link>https://dev.to/ale_annini</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F104337%2F56644bc4-f83b-446e-ba75-d374ae2e1f6c.jpg</url>
      <title>DEV Community: Alessandro Annini</title>
      <link>https://dev.to/ale_annini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ale_annini"/>
    <language>en</language>
    <item>
      <title>AI Writes Better Code When You Stop Thinking in Code</title>
      <dc:creator>Alessandro Annini</dc:creator>
      <pubDate>Tue, 29 Jul 2025 22:40:59 +0000</pubDate>
      <link>https://dev.to/ale_annini/ai-writes-better-code-when-you-stop-thinking-in-code-40nk</link>
      <guid>https://dev.to/ale_annini/ai-writes-better-code-when-you-stop-thinking-in-code-40nk</guid>
      <description>&lt;p&gt;From Chaos to Clarity: Building Software with AI Starts with the Right Map&lt;/p&gt;

&lt;p&gt;[Photo by &lt;a href="https://unsplash.com/@dulhiier?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Nastya Dulhiier&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;]&lt;/p&gt;

&lt;h2&gt;
  
  
  From Code to Concept
&lt;/h2&gt;

&lt;p&gt;Working with AI on software projects is no longer a futuristic dream, it’s already here. But if you’ve tried building something serious with tools like Cursor, Claude Code or Windsurf, you’ve likely hit a wall: when the project is complex the AI gets lost in the code. Too many files, too many side effects, too many implicit assumptions.&lt;/p&gt;

&lt;p&gt;That’s because &lt;strong&gt;code is not the best way to reason about systems for humans or AIs&lt;/strong&gt;. We need a better abstraction layer. Something precise, high-level, shorter and deterministic. That’s where &lt;strong&gt;LayeredDSL&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/AlessandroAnnini/layereddsl" rel="noopener noreferrer"&gt;https://github.com/AlessandroAnnini/layereddsl&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Try yourself:&lt;/strong&gt; copy &lt;a href="https://github.com/AlessandroAnnini/layereddsl/blob/main/layereddsl-specs.md" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; and give it to your AI assistant.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Problem With Code-First AI
&lt;/h2&gt;

&lt;p&gt;When you ask an AI to generate or modify code, it struggles with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ambiguity&lt;/strong&gt;: Codebases are filled with partial implementations, hacks, and assumptions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scale&lt;/strong&gt;: Large projects span hundreds of files... which ones should be changed or even read?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alignment&lt;/strong&gt;: AI doesn’t always understand the &lt;em&gt;why&lt;/em&gt; behind your design so sometimes it change it slightly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt;: It’s hard to check if code is “correct” without rerunning full tests.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result? Fragile interactions, unpredictable outputs, and hallucinated features. Even when it works, you can’t fully trust it. Larger, better models used in agentic fashon solve the problems, but only partially because even if they are enough to manage a large codbase, something could be overlooked and, in the end you will pay a very high price to keep playing with a lot of tokens.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Shift: Map-First, Code-Later
&lt;/h2&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%2Fcdn-images-1.medium.com%2Fmax%2F10936%2F0%2AVi3_hJ_ST83zzqUX" 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%2Fcdn-images-1.medium.com%2Fmax%2F10936%2F0%2AVi3_hJ_ST83zzqUX" alt="Photo by [Venti Views](https://unsplash.com/@ventiviews?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)" width="720" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Framework to work with AI like you would with a agile team are effective and all, and I really think there is a lot of potential there but something is still too clunky, there is too much struggle and it not feels right to me. I was thinking about a way to literally work-around this and I started to wonder what would happen if, instead of reasoning through the code, you described your &lt;strong&gt;system’s intent&lt;/strong&gt; in a structured, formal language? A &lt;strong&gt;Domain-Specific Language (DSL)&lt;/strong&gt; that acts as a single source of truth, designed for collaboration between humans and AI&lt;/p&gt;

&lt;p&gt;That’s the idea behind &lt;strong&gt;LayeredDSL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rather than asking AI to navigate or rewrite your codebase blindly, you collaborate with it &lt;strong&gt;at the design layer&lt;/strong&gt;, where the system is defined clearly and unambiguously. The DSL becomes the contract, and code is just an implementation detail.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Shifting to DSL-first development brings long-term strategic advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Determinism&lt;/strong&gt;: No more guessing what the AI will do: if the DSL is complete and accurate, so will the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🧠 &lt;strong&gt;Clarity&lt;/strong&gt;: Stakeholders, devs, and AIs all work from the same shared model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔁 &lt;strong&gt;Traceability&lt;/strong&gt;: Every logic function is mapped to a real component so nothing is implied or lost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🛠 &lt;strong&gt;Maintenance&lt;/strong&gt;: When requirements change, you change the DSL. AI updates only the affected code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🚀 &lt;strong&gt;Bootstrap speed&lt;/strong&gt;: New modules or microservices can be generated from zero with confidence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔍 &lt;strong&gt;Code validation&lt;/strong&gt;: The AI can also act as a linter, checking if the current code matches the spec.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔐 &lt;strong&gt;Safety&lt;/strong&gt;: Less chance of hallucinations or shadow logic because the DSL is always the source of truth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🌐 &lt;strong&gt;Cross-language portability&lt;/strong&gt;: You can implement the same DSL-defined system in a completely different programming language or framework with minimal changes to the map.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And just as importantly: &lt;strong&gt;a small, focused DSL document outperforms a massive codebase when reasoning with AI&lt;/strong&gt;. Instead of re-reading thousands of tokens just to understand how a system works, the AI can rely on a compact, unambiguous, layered definition. It’s faster, clearer, and more reliable for both humans and machines.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is LayeredDSL?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;LayeredDSL&lt;/strong&gt; is a structured YAML-based metamodel. It doesn’t describe code but instead it describes &lt;strong&gt;projects&lt;/strong&gt;. Any kind of software project: web apps, APIs, microservices, mobile apps, even hybrid systems with or without AI agents.&lt;/p&gt;

&lt;p&gt;It includes layers such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;project: name, version, authors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;domain: data models and types&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;logic: business functions and expected behavior&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;components: service/module boundaries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ui: user-facing pages and controls&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;workflow: ordered or conditional logic sequences&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;security: roles and permissions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;mapping: where logic is implemented&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;metadata: change history&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why It Works: Design Goals
&lt;/h2&gt;

&lt;p&gt;LayeredDSL was designed with the following principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layered by intent:&lt;/strong&gt; business logic is separate from data and UI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Readable:&lt;/strong&gt; every part is easy to understand for both humans and AI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generative + Validatable:&lt;/strong&gt; the same DSL can be used to produce or check code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immutable source of truth:&lt;/strong&gt; AI never freelances beyond what the DSL says&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deterministic mapping:&lt;/strong&gt; same DSL = same code = same behavior&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Summing it all up AI can work with it so confidently. It knows what to do and what not to.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Core Structure of LayeredDSL
&lt;/h2&gt;

&lt;p&gt;Here’s a compact example that defines an invoicing app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;InvoiceManager&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.1.0&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Track invoices and notify clients&lt;/span&gt;

    &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Invoice&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UUID&lt;/span&gt;
        &lt;span class="na"&gt;customerId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;UUID&lt;/span&gt;
        &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;float&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;enum[draft, sent, paid]&lt;/span&gt;
        &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datetime&lt;/span&gt;

    &lt;span class="na"&gt;logic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CreateInvoice&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;customerId&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;amount&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Invoice&lt;/span&gt;
        &lt;span class="na"&gt;modifies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Invoice&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Creates a new invoice.&lt;/span&gt;

    &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;billing_service&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;module&lt;/span&gt;
        &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
        &lt;span class="na"&gt;responsibilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;CreateInvoice&lt;/span&gt;

    &lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dashboard&lt;/span&gt;
          &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
          &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;table&lt;/span&gt;
              &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Invoice&lt;/span&gt;
              &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;customerId&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;amount&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;form&lt;/span&gt;
              &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;customerId&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uuid&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amount&lt;/span&gt;
                  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;float&lt;/span&gt;
              &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateInvoice&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No code yet, just a clear description of what should exist. From here, the AI can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Scaffold the backend&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate UI pages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Validate the current implementation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identify missing logic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auto-create test cases&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No worries about AI creating something you didn’t discuss before and even if this would be the case, you can easily tell the assistant to remove that part from the DSL in no time, using a minimal amount of tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human + AI Cycle: Define → Validate → Generate
&lt;/h2&gt;

&lt;p&gt;Working with LayeredDSL involves a clear three-phase loop:&lt;/p&gt;

&lt;h2&gt;
  
  
  Define
&lt;/h2&gt;

&lt;p&gt;The human developer discuss the idea with the AI that drafts the DSL file. AI assists by suggesting completions or pointing out gaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate
&lt;/h2&gt;

&lt;p&gt;When the DSL map is complete the AI checks whether existing code satisfies the map. If everything matches nothing more is needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate
&lt;/h2&gt;

&lt;p&gt;If something is missing, the AI generates &lt;strong&gt;only&lt;/strong&gt; the missing code, and &lt;strong&gt;only&lt;/strong&gt; according to the DSL. No freelancing. No feature creep.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Think of it like building software from a precise architectural drawing, not a fuzzy conversation.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This cycle can be repeated across sprints, services, or even different teams. The DSL becomes your living contract with the AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The “One Rule”
&lt;/h2&gt;

&lt;p&gt;Layered DSL map and code should always be aligned, if they are not, ask your trusted assistant to align them by modifying the DSL or the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples: Real-World DSL in Action
&lt;/h2&gt;

&lt;p&gt;Here’s a &lt;em&gt;simplified&lt;/em&gt; workflow example from a CRM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;workflow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LeadProcessing&lt;/span&gt;
        &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ValidateLead&lt;/span&gt;
            &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lead.source == "web"&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NotifySalesTeam&lt;/span&gt;
            &lt;span class="na"&gt;loop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;each user in SalesUser[]&lt;/span&gt;
            &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user.region == lead.region&lt;/span&gt;

          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lead.score &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;
                  &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AssignPriorityRep&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lead.score &amp;lt;= &lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;
                  &lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AddToNewsletter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure captures control flow, decisions, and team logic in a way that is &lt;strong&gt;trivial for AI to interpret&lt;/strong&gt; and &lt;strong&gt;clear to every human&lt;/strong&gt; on the team.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Prompt Engineering for DSL
&lt;/h2&gt;

&lt;p&gt;Because LayeredDSL is so explicit, you can talk to your AI assistant in terms like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;“Suggest missing workflow steps based on the logic defined.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;“Create backend code for everything in this DSL file.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;“Does CreateInvoice already exist in billing_service?”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;“Generate a test plan for this logic based on inputs and output.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;“Refactor this DSL to separate customer-facing and admin UI.”&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’re no longer wrangling unpredictable LLM behavior when the code is at stake you’re &lt;strong&gt;using AI to make development much more deterministic by planning this on paper, with a structured document.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;AI won’t replace developers. But it will change the way we &lt;strong&gt;describe, evolve, and verify&lt;/strong&gt; software.&lt;/p&gt;

&lt;p&gt;Many frameworks are being created to fragment ai working into specialized agents and this is super effective, but they would be a lot more effective if they could &lt;strong&gt;plan the work ahead and know in advance the full picture&lt;/strong&gt; and not “blinsided” by some patchwork code they find on the code when they try to complete a task.&lt;/p&gt;

&lt;p&gt;By moving from code to concept by &lt;strong&gt;describing intent instead of implementation&lt;/strong&gt; you unlock a new level of AI-assisted development power.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;LayeredDSL&lt;/strong&gt; might just be the missing bridge.&lt;/p&gt;

&lt;h2&gt;
  
  
  👨🏻‍💻 About the Author
&lt;/h2&gt;

&lt;p&gt;I’m &lt;strong&gt;Alessandro Annini&lt;/strong&gt;, a software engineer with 15+ years of experience across sectors from aviation entertainment systems to large-scale platforms. My work is grounded in one goal: &lt;strong&gt;make powerful tech accessible and practical&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://github.com/AlessandroAnnini" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://linkedin.com/in/alessandroannini" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; to keep up with the future of portable development.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Lightning-Fast Development with Zed and Dev Containers</title>
      <dc:creator>Alessandro Annini</dc:creator>
      <pubDate>Sat, 26 Oct 2024 13:50:30 +0000</pubDate>
      <link>https://dev.to/ale_annini/lightning-fast-development-with-zed-and-dev-containers-1nbd</link>
      <guid>https://dev.to/ale_annini/lightning-fast-development-with-zed-and-dev-containers-1nbd</guid>
      <description>&lt;p&gt;Ever found yourself saying “but it works on my machine” or spent hours setting up a development environment for a new team member? Dev Containers might just be the solution you’re looking for, and now you can use them with the &lt;em&gt;blazingly&lt;/em&gt; fast Zed editor.&lt;/p&gt;

&lt;h1&gt;
  
  
  What’s a Dev Container?
&lt;/h1&gt;

&lt;p&gt;Think of a &lt;a href="https://containers.dev/" rel="noopener noreferrer"&gt;dev container&lt;/a&gt; as a pre-configured development environment in a box. It’s a Docker container that comes with all the tools, libraries, and configurations your project needs. The beauty is that it’s consistent across all machines — whether you’re coding on a MacBook Pro, a Windows desktop, or a Linux laptop.&lt;/p&gt;

&lt;p&gt;Dev Containers can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Programming languages and runtimes&lt;/li&gt;
&lt;li&gt;Build tools and package managers&lt;/li&gt;
&lt;li&gt;Environment variables and settings&lt;/li&gt;
&lt;li&gt;Extensions and tooling&lt;/li&gt;
&lt;li&gt;Even GUI applications!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best part? Everything is defined in code through a “devcontainer.json*”* file, making it version-controlled and shareable with your team.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Should You Care?
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding Made Easy&lt;/strong&gt;: New team member? “Clone the repo and start the dev container” — that’s it. No more multi-page setup docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency is King&lt;/strong&gt;: Everyone on the team uses exactly the same environment. No more “works on my machine” syndrome.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation is Bliss&lt;/strong&gt;: Each project can have its own contained environment without conflicting with other projects or your system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD Parity&lt;/strong&gt;: Your development environment can match your CI/CD pipeline exactly, reducing deployment surprises.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Enter Zed Tasks
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://zed.dev" rel="noopener noreferrer"&gt;Zed&lt;/a&gt; is a high-performance code editor that’s gaining popularity for its speed and simplicity. One of its powerful features is &lt;a href="https://zed.dev/docs/tasks" rel="noopener noreferrer"&gt;Tasks&lt;/a&gt; — a way to define and run commands directly from the editor.&lt;/p&gt;

&lt;p&gt;Zed Tasks allow you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define reusable commands in JSON&lt;/li&gt;
&lt;li&gt;Run them with keyboard shortcuts&lt;/li&gt;
&lt;li&gt;See output in an integrated terminal&lt;/li&gt;
&lt;li&gt;Access editor context (like current file path or selected text)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Setup Dev Containers + Zed Tasks
&lt;/h1&gt;

&lt;p&gt;While VS Code has built-in support for dev containers, we can achieve similar functionality in Zed using Tasks and the &lt;a href="https://github.com/devcontainers/cli" rel="noopener noreferrer"&gt;Dev Container CLI&lt;/a&gt;. Here’s how to set it up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install the Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, install the Dev Container CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @devcontainers/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Set Up Your Dev Container&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.devcontainer/devcontainer.json\&lt;/code&gt; file in your project root. Here’s a simple example for a Node.js project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "Node.js Project",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bookworm",
  "forwardPorts": [
    3000
  ],
  "postCreateCommand": "npm install",
  "postStartCommand": "npm run dev"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Configure Zed Tasks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.zed/tasks.json\&lt;/code&gt; file in your project root with these tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "label": "DevContainer: Up",
    "command": "devcontainer up - workspace-folder ${ZED_WORKTREE_ROOT}",
    "use_new_terminal": true,
    "allow_concurrent_runs": false,
    "reveal": "always"
  },
  {
    "label": "DevContainer: Build",
    "command": "devcontainer build - workspace-folder ${ZED_WORKTREE_ROOT}",
    "use_new_terminal": true,
    "allow_concurrent_runs": false,
    "reveal": "always"
  },
  {
    "label": "DevContainer: Exec",
    "command": "devcontainer exec - workspace-folder ${ZED_WORKTREE_ROOT} ${1}",
    "use_new_terminal": true,
    "allow_concurrent_runs": true,
    "reveal": "always"
  },
  {
    "label": "DevContainer: Run User Commands",
    "command": "devcontainer run-user-commands - workspace-folder ${ZED_WORKTREE_ROOT}",
    "use_new_terminal": true,
    "allow_concurrent_runs": false,
    "reveal": "always"
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Using Your Dev Container&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1. Open your project in Zed\&lt;br&gt;
2. Press &lt;code&gt;Cmd/Ctrl + Shift + P\&lt;/code&gt; to open the command palette\&lt;br&gt;
3. Type &lt;code&gt;task: spawn\&lt;/code&gt; and select it\&lt;br&gt;
4. Choose “DevContainer: Build” to build your container\&lt;br&gt;
5. Use “DevContainer: Up” to start it\&lt;br&gt;
6. Run commands inside your container with “DevContainer: Exec”&lt;/p&gt;
&lt;h1&gt;
  
  
  Understanding Container Lifecycle Commands
&lt;/h1&gt;

&lt;p&gt;Your dev container can automate setup and initialization through lifecycle commands. These commands run at different stages of your container’s lifecycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;postCreateCommand&lt;/strong&gt;\
Runs once after the container is created. Perfect for initial setup:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "postCreateCommand": "npm install &amp;amp;&amp;amp; npm run db:migrate"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;2. postStartCommand&lt;/strong&gt;\&lt;br&gt;
Runs every time the container starts. Ideal for development servers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "postStartCommand": {
    "server": "npm run dev",
    "db": "docker run postgres",
    "watch": "npm run watch"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. postAttachCommand&lt;/strong&gt;\&lt;br&gt;
Runs when you connect to the container. Good for session setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "postAttachCommand": "source ~/.nvm/nvm.sh"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To execute these commands in Zed, use the “&lt;strong&gt;DevContainer: Run User Commands”&lt;/strong&gt; task after starting your container.&lt;/p&gt;

&lt;h1&gt;
  
  
  Pro Tips
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;1. Custom Key Bindings&lt;/strong&gt;: Add this to your &lt;em&gt;keymap.json&lt;/em&gt; to quickly spawn your container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "context": "Workspace",
  "bindings": {
    "alt-d u": [
      "task::Spawn",
      {
        "task_name": "DevContainer: Up"
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. One-Shot Commands&lt;/strong&gt;: Need to run a quick command? Use the task spawn modal and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;devcontainer exec — workspace-folder ${ZED_WORKTREE_ROOT} npm test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Task Variables&lt;/strong&gt;: Use Zed’s built-in variables like &lt;code&gt;${ZED\_FILE}\&lt;/code&gt; to reference the absolute path of the currently opened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Combined Startup&lt;/strong&gt;: Create a single task for starting development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "label": "DevContainer: Start Development",
  "command": "devcontainer up - workspace-folder ${ZED_WORKTREE_ROOT} &amp;amp;&amp;amp; devcontainer run-user-commands - workspace-folder ${ZED_WORKTREE_ROOT}",
  "use_new_terminal": true,
  "allow_concurrent_runs": false,
  "reveal": "always"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;While this setup requires a bit more configuration than VS Code’s integrated solution, it brings the power of dev containers to Zed’s lightning-fast editor. You get the best of both worlds: Zed’s performance and simplicity, combined with the consistency and isolation of dev containers.&lt;/p&gt;

&lt;p&gt;The dev container ecosystem is growing rapidly, with support from major players like GitHub, Microsoft, and others. By setting up this workflow in Zed, you’re future-proofing your development environment while keeping it &lt;em&gt;blazingly&lt;/em&gt; fast.&lt;/p&gt;

&lt;p&gt;Give it a try, and you might find that dev containers + Zed is the workflow you’ve been looking for all along.&lt;/p&gt;

&lt;p&gt;Want to learn more? Check out the Dev Container Specification and Zed’s documentation.&lt;/p&gt;




&lt;p&gt;This is me on LinkedIn, Twitter and Github.&lt;/p&gt;

</description>
      <category>zed</category>
      <category>devcontainers</category>
      <category>software</category>
      <category>development</category>
    </item>
    <item>
      <title>What’s my AGI again? Implementing an Autonomous Agent in Js</title>
      <dc:creator>Alessandro Annini</dc:creator>
      <pubDate>Mon, 23 Oct 2023 14:45:20 +0000</pubDate>
      <link>https://dev.to/ale_annini/whats-my-agi-again-implementing-an-autonomous-agent-in-js-39b4</link>
      <guid>https://dev.to/ale_annini/whats-my-agi-again-implementing-an-autonomous-agent-in-js-39b4</guid>
      <description>&lt;p&gt;[This article was first published on &lt;a href="https://alessandro-annini.medium.com/whats-my-agi-again-implementing-an-autonomous-agent-in-js-e05d73cf532c" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;]&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I fired it up it was a coding night,&lt;br&gt;
I used the key to get the AI right, &lt;br&gt;
I started sending text, And it processed my lines, &lt;br&gt;
But then it called a service, see?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;😄&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: This agent library can use functions and call on itself until it finds a way to answer to the user. It’s not a real, complete &lt;a href="https://en.wikipedia.org/wiki/Artificial_general_intelligence" rel="noopener noreferrer"&gt;**artificial general intelligence&lt;/a&gt;*&lt;em&gt;, it’s really extremely *far&lt;/em&gt; from it actually, but it’s a nice experiment in that direction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/AlessandroAnnini/micro-agi-js" rel="noopener noreferrer"&gt;**Github repo for micro-agi-js&lt;/a&gt; — this article is based on &lt;a href="https://github.com/AlessandroAnnini/micro-agi-js/tree/v1.0.2" rel="noopener noreferrer"&gt;v1.0.2&lt;/a&gt;**&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%2Fcdn-images-1.medium.com%2Fmax%2F12000%2F0%2AK1UZrQnEy-WBfHXO" 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%2Fcdn-images-1.medium.com%2Fmax%2F12000%2F0%2AK1UZrQnEy-WBfHXO" alt="Photo by [Lyman Hansel Gerona](https://unsplash.com/@lhgerona?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
[Photo by &lt;a href="https://unsplash.com/@lhgerona?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Lyman Hansel Gerona&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Definition of AGI in 99 words:&lt;/strong&gt;&lt;br&gt;
Artificial General Intelligence (AGI) refers to machines that possess the ability to understand, learn, and apply knowledge across a wide range of tasks, akin to human intelligence. Unlike narrow AI, which excels at specific tasks, an AGI can adapt to various challenges without prior programming. It involves complex problem-solving, reasoning, and comprehension, aiming to mimic human cognitive functions. AGI’s development is a monumental task, requiring advancements in machine learning, neuroscience, and computer science. While it holds tremendous potential, including automating complex tasks and enhancing problem-solving, it also raises ethical and safety concerns that necessitate careful consideration and robust safeguards.&lt;/p&gt;

&lt;h2&gt;
  
  
  It is able to &lt;em&gt;reason&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Actually the word “reason” is (still) misleading in the field of AI but with the AGI technique the LLM can generate a chain of thought. This is important when they cannot achieve their goal in a single iteration. This gives space for the AI to understand what it needs to satisfy the request.&lt;/p&gt;

&lt;p&gt;Every time the script calls OpenAI it uses function calling feature to get back a structured response that can be parsed from JSON to a Javascript object.&lt;/p&gt;

&lt;p&gt;This object contains 4 standard properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;thought&lt;/strong&gt;: the main idea the LLM comes up with&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;description&lt;/strong&gt;: an expanded version of the thought where the LLM elaborates more&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;criticism&lt;/strong&gt;: something that could be improved or some information that is missing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;response:&lt;/strong&gt; (optional) the final response that will be presented to the user&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The “response” property is optional because the AI could not have an answer after the iteration so the other properties (thought, description and criticism) are given to the next iteration so the AI can continue to reason from where it left.&lt;/p&gt;

&lt;h2&gt;
  
  
  It is able to use tools
&lt;/h2&gt;

&lt;p&gt;Another extremely powerful feature is the ability to use the tools that you teach them. More specifically you could have some functions that could help the AI during the reasoning, maybe those functions are loading data from a database or maybe they hit an API to get useful data.&lt;/p&gt;

&lt;p&gt;Tools are functions.&lt;/p&gt;

&lt;p&gt;If you want to make those functions available to the AI you need to describe how to use them, and you can do this using OpenAI function calling feature.&lt;/p&gt;

&lt;p&gt;The aim is to create a clear list that explains how your tools work. For each tool, mention its &lt;em&gt;name&lt;/em&gt; and describe &lt;em&gt;what it does&lt;/em&gt;. If the tool needs extra information (&lt;em&gt;parameters&lt;/em&gt;) to work, list these details too, including each one’s name, what it should be like (&lt;em&gt;type&lt;/em&gt;), a short explanation of what it does, and whether it’s essential to use the tool properly. This way, you have a straightforward guide to help use your tools correctly.&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%2Fpwhejgk3f1vxx76n70ak.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%2Fpwhejgk3f1vxx76n70ak.png" alt="My function definitions. These will be the tools available to the agent." width="800" height="827"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the example above I am trying to teach the LLM how my functions work giving them the most minimal but complete information set about them so that when it needs something that it doesn’t know, it looks for function that could help and then is able to tell you what to execute and how, giving you the parameters, if any.&lt;/p&gt;

&lt;p&gt;An important concept here is that the LLM is always the brain only, and you are in charge to build the arms and teach it how to use them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;YOU&lt;/strong&gt; create the functions you need to get data or whatever&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;YOU&lt;/strong&gt; create an object like the example above to describe your functions to the AI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;YOU&lt;/strong&gt; send the functions definitions + your prompt to the AI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI&lt;/strong&gt; tells you which function is needed and the parameters to use&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;YOU&lt;/strong&gt; execute the corresponding function with the parameters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;strong&gt;result&lt;/strong&gt; is obtained&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This OpenAI feature is really powerful as it is but it looks like magic when working in a AGI workflow!&lt;/p&gt;

&lt;p&gt;The mission is to be able to obtain from the LLM a response that always have thought, description and criticism properties to keep alive the chain of thoughts but to the 4 main AGI properties I added a new, optional, one: **“command”. **How do I define this? Using my functions definitions.&lt;/p&gt;

&lt;p&gt;This way the LLM can return a response for the user or can return a command to execute or maybe neither of the two because it needs an iteration to think.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introspection
&lt;/h2&gt;

&lt;p&gt;This is the typical AGI function description to have some kind of introspection about the AI thinking:&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%2Fphsf7dmljih7vmfa8u86.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%2Fphsf7dmljih7vmfa8u86.png" alt="This version has “command” property removed, for clarity." width="800" height="748"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the part that makes possible a “reasoning” for our agent, but here, for clarity, I removed the &lt;em&gt;command part.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;command&lt;/em&gt; property will hold what makes possible for the user to nest the definitions for their tools/functions on the previous part of the structure, the one that provides for the reasoning. This new part introduces our functions and how to use them to the agent so it can reason about if, when and how to use them.&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%2Frslp94fywr3ql7x4j06i.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%2Frslp94fywr3ql7x4j06i.png" alt="This version has “command” property only, for clarity." width="800" height="751"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the first image we saw how to define my functions in a way that the agent can use them passing the definitions to OpenAI API using the feature “function calling”, well, that objet is nested right here inside the command property, so OpenAI is able to &lt;strong&gt;decide if it needs to ask you to use a function to get some data and how you should use it in order to get the data it needs&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Agent
&lt;/h2&gt;

&lt;p&gt;The engine of the agent is fairly simple: it exposes a function able to receive a message from outside. This message is thrown into a recursive fuction with the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Uses OpenAI completion feature ALWAYS using “function calling”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unpacks the response from OpenAI and check if a command is present&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the response contains a **command property **it tries to execute it using the services that the user passed when the agent was created. This command and the respose of the function are then appended to the context that the agent sends to OpenAI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the response contains a &lt;strong&gt;response propery&lt;/strong&gt; then the content will be appended to the context and used to respond to the user&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the response does not contain &lt;strong&gt;any command nor response property&lt;/strong&gt;, then the content is appended to the context and the recursive function calls itself for a new iteration&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9zttiafrxlqrgqh4sju.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%2Fs9zttiafrxlqrgqh4sju.png" alt="Agent working flow" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding the agent
&lt;/h2&gt;

&lt;p&gt;Let’s now see the code and unpack it step by step.&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%2Fqpm0raa93sma5wdddum2.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%2Fqpm0raa93sma5wdddum2.png" alt="full simplified code" width="800" height="1590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I create an agent i get an object with a signle function exposed: *processMessage. *We will begin from here.&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%2Fnb372lojqyrto2gdz9o8.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%2Fnb372lojqyrto2gdz9o8.png" alt="processMessage" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This function gets a message from the user as input, add it to the current context as a user message and call a recursive function: *recurseUntilResponse *that, when executed for first thing calls OpenAI API to understand how to procede&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%2Fjjubxezj7odg1dye4l2g.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%2Fjjubxezj7odg1dye4l2g.png" alt="fetch OpenAI response" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;fetchOpenAIResponse will use OpenAI &lt;em&gt;completion&lt;/em&gt; and &lt;em&gt;function calling&lt;/em&gt; with the functions that we have built in the first part of the article, then after having received a response, it checks if the response contains a &lt;strong&gt;command&lt;/strong&gt; to execute or a &lt;strong&gt;response&lt;/strong&gt; for the user.&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%2Fjrcq4er9nzhyi0in1o6h.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%2Fjrcq4er9nzhyi0in1o6h.png" alt="a command is found" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a command is found it means that I now have the name of a function to call and the necessary arguments (I am talking about the nested function of the first snippet of code).&lt;/p&gt;

&lt;p&gt;After the selected function is executed I add the result to the context and, thanks to the recursion, it will be included in the next iteration and sent to the next call to OpenAI that (hopefully) will use the result to formulate a response for the user.&lt;br&gt;
If the data is still not what is needed to respond to the user, the LLM could decide to call another function during the next iteration.&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%2Fcd2bumwk1n53a07qh209.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%2Fcd2bumwk1n53a07qh209.png" alt="a response is found" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a response is found it means that the LLM found a way to answer to the user, maybe because the context was containing enough data (thanks to previous command results included in it).&lt;/p&gt;

&lt;p&gt;The recursion stops here, the response itself is added to the context, this time with role &lt;em&gt;assistant,&lt;/em&gt; and sent to the user.&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%2Fehd2j9q4idui2jto4v6j.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%2Fehd2j9q4idui2jto4v6j.png" alt="neither a command or a respose is found" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes you will get no command or response, this happens when the data is not enough to answer to the user and maybe the LLM needs more reasoning or realize that it needs more data before it can formulate a response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&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%2Fcdn-images-1.medium.com%2Fmax%2F6736%2F0%2Ad1wwiL8LeBCABE62" 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%2Fcdn-images-1.medium.com%2Fmax%2F6736%2F0%2Ad1wwiL8LeBCABE62" alt="Photo by [Rock'n Roll Monkey](https://unsplash.com/@rocknrollmonkey?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)" width="800" height="911"&gt;&lt;/a&gt;&lt;br&gt;
[Photo by &lt;a href="https://unsplash.com/@rocknrollmonkey?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Rock'n Roll Monkey&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;This is, in brief, the anatomy of *micro-agi-js. *The library is actually a simple, experimental implementation and many improvements can be added to it. At the same time the results are quite effective and it is often capable to use many functions at the right time, the right way. This, in my opinion enables many new, useful, paradigms in a wide range of scenarios in a way that is absolutely fascinating, technically effective and still a powerful marketing weapon.&lt;/p&gt;

&lt;p&gt;I am already working to a new implementation that includes limits to the API calling, embeddings and the ability to have a conversation between different agents.&lt;/p&gt;

&lt;p&gt;Try the library &lt;a href="https://github.com/AlessandroAnnini/micro-agi-js/blob/3f70b9473a3152abc4f2dbcc25da15fd3e3e2832/example/index.js" rel="noopener noreferrer"&gt;looking at the example&lt;/a&gt; and let me know what do you think about it and how would you change it.&lt;/p&gt;

&lt;p&gt;This is me on &lt;a href="https://www.linkedin.com/in/alessandroannini/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, &lt;a href="https://twitter.com/ale_annini" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/AlessandroAnnini" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>PUSHARD Progressively deploy commits between AWS Amplify environments</title>
      <dc:creator>Alessandro Annini</dc:creator>
      <pubDate>Fri, 03 Dec 2021 17:07:22 +0000</pubDate>
      <link>https://dev.to/ale_annini/pushard-progressively-deploy-commits-between-aws-amplify-environments-4mjj</link>
      <guid>https://dev.to/ale_annini/pushard-progressively-deploy-commits-between-aws-amplify-environments-4mjj</guid>
      <description>&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;When creating a new project with AWS Amplify life is good: you have a lot of different AWS services handy and ready to be used in a integrated way. If you ever used Amplify you know what we are talking about.&lt;br&gt;
Everything is fine until you begin to use &lt;a href="https://docs.amplify.aws/cli/teams/overview/" rel="noopener noreferrer"&gt;Amplify environments&lt;/a&gt;. This is because Amplify creates a copy of every service you are using for every environment you are creating. When it comes to Appsync there is no exception.&lt;/p&gt;

&lt;p&gt;If you need different environment like &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt; (maybe you even have &lt;code&gt;demo&lt;/code&gt;!) you will find yourself with three times your cognito pools (if you have authentication), three times your lambdas and three times your graphql tables. Often times when you are developing, you will have to make changes to your &lt;code&gt;schema.graphql&lt;/code&gt; but you don't want to reflect those changes in other environments right away because maybe the UI is still not updated or simply because your work isn't completly done yet and you don't want to deploy a broken version.&lt;br&gt;
The problem you can incur into is a known Amplify problem, in fact AWS even has a section on their docs about it: &lt;a href="https://docs.amplify.aws/cli-legacy/graphql-transformer/key/#deploying-multiple-secondary-indices-gsi" rel="noopener noreferrer"&gt;Deploying multiple secondary indices (GSI)&lt;/a&gt;, and their github repository has a lot of issues about this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Attempting to mutate more than 1 global secondary index at the same time on the &amp;lt;table_name&amp;gt; table in the stack.
An error occurred during the push operation: Attempting to mutate more than 1 global secondary index at the same time on the &amp;lt;table_name&amp;gt; table in the  stack.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you try to modify 2 or more connections on the same table, when you try to push your changes to Cloud Formation with the command &lt;code&gt;amplify push&lt;/code&gt;, you will get this error. When you're working on the development branch is not a big deal because you only have to make multiple "amplify push" in order to gradually change the connections state.&lt;/p&gt;

&lt;p&gt;The REAL problem comes after you have made a number of amplify push on your development environment and you want to replicate the final schema.graphql to the test or production environment. Suppose you made 10 amplify push on your development environment but your production environment has never been updated after push number 3: now if you try to move your current schema.graphql (let's call it version 10) over your schema.graphql version 3 on production, you will have a big problem. Amplify won't be able to make multiple updates on the secondary indexes of your production tables just like it wasn't before, when you was trying to make only two updates together on the same table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You would have to replicate the exact sequence of successful updates you made on you development environment to you production one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here comes into play PUSHARD!&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%2Fraw.githubusercontent.com%2Fnautes-tech%2Fpushard%2Fmain%2Fimages%2Fflow.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%2Fraw.githubusercontent.com%2Fnautes-tech%2Fpushard%2Fmain%2Fimages%2Fflow.png" title="git-action flow" alt="git-action flwo" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First of all &lt;strong&gt;you have to&lt;/strong&gt; make a commit on git after &lt;strong&gt;every&lt;/strong&gt; &lt;code&gt;amplify push&lt;/code&gt; on your environment.&lt;br&gt;
This Git Action will then take care to grab your commit hisory from development branch, detect the exact slice of commits you need to fully replicate the development verison of schema.graphql to your target environment. In order to deploy every update it will try to merge the current development commit into target branch, launch &lt;code&gt;amplify push&lt;/code&gt; command and finally &lt;code&gt;git commit&lt;/code&gt; the result after every successful operation on the target branch.&lt;br&gt;
If something shoud go south during the process, it will push to git everything is committed at the time, the next time it will try to pick up the process where it left off.&lt;/p&gt;

&lt;p&gt;Now, this flow is correct from a AWS standpoint because DynamoDB will be able to update step by step its tables. But at the same time, when asking many consecutive Cloud Formation updates, especially if you created or updated a lambda in your stack too, will take longer than the time of the simple &lt;code&gt;amplify push&lt;/code&gt; command, so, at the next iteragion of updates loop, you could have another error:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resource is not in the state stackUpdateComplete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This means that Cloud Formation has not yet completely update your lambda resources and, because of this, it won't be able to make further updates.&lt;br&gt;
Because of this you &lt;strong&gt;have to&lt;/strong&gt; make a commit after every time you create or modify a lambda function. This way our Git Action will be able to successfully push the change to amplify and during the next loop iteration it will wait for Cloud Formation to be in the &lt;code&gt;UPDATE_COMPLETE&lt;/code&gt; state before trying to update the stack again.&lt;/p&gt;

&lt;p&gt;When the git action is starting it can send you a Telegram message writing how many commits it will take from development to the target branch; it will message you after every successful &lt;code&gt;amplify push&lt;/code&gt; operation and if something goes wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nautes-tech/pushard/blob/main/LICENSE" rel="noopener noreferrer"&gt;LICENSE&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/nautes-tech/pushard/blob/main/README.md" rel="noopener noreferrer"&gt;README&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/nautes-tech" rel="noopener noreferrer"&gt;
        nautes-tech
      &lt;/a&gt; / &lt;a href="https://github.com/nautes-tech/pushard" rel="noopener noreferrer"&gt;
        pushard
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Progressively deploy commits between AWS Amplify environments
    &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;PUSHARD Progressively deploy commits between AWS Amplify environments&lt;/h1&gt;
&lt;/div&gt;



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

&lt;p&gt;When creating a new project with AWS Amplify life is good: you have a lot of different AWS services handy and ready to be used in a integrated way. If you ever used Amplify you know what we are talking about
Everything is fine until you begin to use &lt;a href="https://docs.amplify.aws/cli/teams/overview/" rel="nofollow noopener noreferrer"&gt;Amplify environments&lt;/a&gt;. This is because Amplify creates a copy of every service you are using for every environment you are creating. When it comes to Appsync there is no exception.&lt;/p&gt;
&lt;p&gt;If you need different environment like &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt; (maybe you even have &lt;code&gt;demo&lt;/code&gt;!) you will find yourself with three times your cognito pools (if you have authentication), three times your lambdas and three times your graphql tables. Often times when you are developing, you will have to make changes to your &lt;code&gt;schema.graphql&lt;/code&gt; but you don't want to…&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/nautes-tech/pushard" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;DIY Deployments, Maintainer Must-Haves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Yaml File or Link to Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pushard&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;deployHash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Target&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;deploy&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hash'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;skipHashes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Deploys&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;skip'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;dstBranch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Destination&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;branch'&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;run_pushard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run PUSHARD&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js environment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2.1.5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;14'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create working dir&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mkdir tempdir&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install AWS CLI version &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"&lt;/span&gt;
          &lt;span class="s"&gt;sudo unzip awscliv2.zip&lt;/span&gt;
          &lt;span class="s"&gt;sudo ./aws/install&lt;/span&gt;
          &lt;span class="s"&gt;sudo rm -rf awscliv2.zip&lt;/span&gt;
          &lt;span class="s"&gt;sudo rm -rf aws&lt;/span&gt;
          &lt;span class="s"&gt;aws --version&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install @aws-amplify/cli node package&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g @aws-amplify/cli@7.3.2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PUSHARD&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "Target deploy hash: ${{ github.event.inputs.deployHash }}"&lt;/span&gt;
          &lt;span class="s"&gt;echo "Deploys to skip: ${{ github.event.inputs.skipHashes }}"&lt;/span&gt;
          &lt;span class="s"&gt;echo "Destination branch: ${{ github.event.inputs.dstBranch }}"&lt;/span&gt;
          &lt;span class="s"&gt;cd builder&lt;/span&gt;
          &lt;span class="s"&gt;npm install&lt;/span&gt;
          &lt;span class="s"&gt;npm start&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DEPLOY_HASH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.inputs.deployHash }}&lt;/span&gt;
          &lt;span class="na"&gt;SKIP_HASHES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.inputs.skipHashes }}&lt;/span&gt;
          &lt;span class="na"&gt;DST_BRANCH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.inputs.dstBranch }}&lt;/span&gt;
          &lt;span class="na"&gt;AMPLIFY_PROJECT_REPO&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AMPLIFY_PROJECT_REPO }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ACCESS_KEY_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SECRET_ACCESS_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_PROFILE }}&lt;/span&gt;
          &lt;span class="na"&gt;GH_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GH_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;GH_USER_PAT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GH_USER_PAT }}&lt;/span&gt;
          &lt;span class="na"&gt;TELEGRAM_BOT_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TELEGRAM_BOT_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;TELEGRAM_CHAT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TELEGRAM_CHAT_ID }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://github.com/nautes-tech/pushard/blob/main/action.yml" rel="noopener noreferrer"&gt;action.yml&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;Made by &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__767320"&gt;
    &lt;a href="/simoneagostinelli" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&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%2Fuser%2Fprofile_image%2F767320%2F62e2eda5-1b42-492a-8121-b67852bcd260.jpg" alt="simoneagostinelli image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/simoneagostinelli"&gt;Simone Agostinelli&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/simoneagostinelli"&gt;&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag__user ltag__user__id__104337"&gt;
    &lt;a href="/ale_annini" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&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%2Fuser%2Fprofile_image%2F104337%2F56644bc4-f83b-446e-ba75-d374ae2e1f6c.jpg" alt="ale_annini image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ale_annini"&gt;Alessandro Annini&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ale_annini"&gt;Side projects will save the world. Or at least the day.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.nautes.com/" rel="noopener noreferrer"&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%2Fuhteljoxxeftzfxu24ei.png" alt="Nautes Spa" width="400" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>actionshackathon21</category>
      <category>aws</category>
      <category>github</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Replace Redux with React Hooks, the easy way.</title>
      <dc:creator>Alessandro Annini</dc:creator>
      <pubDate>Tue, 26 Nov 2019 13:41:43 +0000</pubDate>
      <link>https://dev.to/ale_annini/replace-redux-with-react-hooks-the-easy-way-10nk</link>
      <guid>https://dev.to/ale_annini/replace-redux-with-react-hooks-the-easy-way-10nk</guid>
      <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@omerrana?utm_source=medium&amp;amp;utm_medium=referral"&gt;Omer Rana&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Do you ever wonder if you can really replace Redux with React Hooks when it comes to the global state management of your applications, even the most complex?&lt;br&gt;
It happens to me all the time I need to store some data so I decided not to wait a super-definitive opinion from the community but try firsthand.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What the hell, I should try anyway if it fits the way I work!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I love using GraphQL but, when I develop a complex application, I still need some kind of global store, so I started searching for articles about how to replace redux store with the &lt;code&gt;useContext&lt;/code&gt; React hook. I found a few, &lt;a href="https://medium.com/r/?url=https%3A%2F%2Fwww.sitepoint.com%2Freplace-redux-react-hooks-context-api%2F"&gt;this one is very inspiring&lt;/a&gt; but I want it to be even cleaner.&lt;/p&gt;

&lt;p&gt;The solution I want has to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎣 using React hooks;&lt;/li&gt;
&lt;li&gt;🚫 without switch statements;&lt;/li&gt;
&lt;li&gt;😌 damn simple and clean;&lt;/li&gt;
&lt;li&gt;🚀 damn fast to use;&lt;/li&gt;
&lt;li&gt;♻️ reusable.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  TLDR; This is what I got:
&lt;/h2&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/nqg9i"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Woah Slow Down
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 0
&lt;/h3&gt;

&lt;p&gt;When I create the Store with &lt;em&gt;Store.js&lt;/em&gt; i need the &lt;code&gt;initialState&lt;/code&gt; and the &lt;code&gt;reducers&lt;/code&gt;, the latter has to be defined as a single object where every single property is a reducer function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ra8ZcLqI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/8kr346ll2x1vq3g7h9l0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ra8ZcLqI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/8kr346ll2x1vq3g7h9l0.png" alt="Alt Text" width="880" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;createStoreProvider&lt;/code&gt; high order function returns another function that has, as only parameter, the inner component for the Store provider so that you can nest in that point the components that will have access to the global state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1
&lt;/h3&gt;

&lt;p&gt;The body of the second function sets up a way to retrieve and use the right reducer function from the initial reducers object and uses it with the &lt;code&gt;useReducer&lt;/code&gt; &lt;a href="https://reactjs.org/docs/hooks-reference.html#usereducer"&gt;React hook&lt;/a&gt; so that every action will change the global state in its own way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2FuPcsw0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zf43opir69tiqxiukt1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2FuPcsw0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zf43opir69tiqxiukt1i.png" alt="Alt Text" width="880" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2
&lt;/h3&gt;

&lt;p&gt;The store &lt;a href="https://reactjs.org/docs/hooks-reference.html#usecontext"&gt;context&lt;/a&gt; that we will use as global state will now be used as &lt;a href="https://reactjs.org/docs/context.html#contextprovider"&gt;context provider&lt;/a&gt;; it will provides an object with 2 properties: the global state itself and a function that we will use to change the state:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KnTxLGvW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6ggbm66c1f2bb0fb7dfv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KnTxLGvW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6ggbm66c1f2bb0fb7dfv.png" alt="Alt Text" width="880" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it.&lt;br&gt;
&lt;a href="https://gist.github.com/AlessandroAnnini/c29b38a1c22eaebd5b5578828288dd6c"&gt;Github gist&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3 (use it!)
&lt;/h3&gt;

&lt;p&gt;When we want to use the state in a component we only need to import the created Store and read the needed property with the help of the &lt;code&gt;useContext&lt;/code&gt; React hook:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QKsvI87U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hwp67eyvwe2yukcfbq3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QKsvI87U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hwp67eyvwe2yukcfbq3l.png" alt="Alt Text" width="880" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Destructuring the store context we can access to both the current state and the &lt;code&gt;dispatch&lt;/code&gt; function that can be used to send changes to our store selecting&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;what&lt;/em&gt; to change thanks to the action &lt;em&gt;type&lt;/em&gt; property and&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;how&lt;/em&gt; to change state thanks to the &lt;em&gt;payload&lt;/em&gt; property
just like we used to do in redux.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  ⚠️ Downsides
&lt;/h2&gt;

&lt;p&gt;When creating React context as global store you have to be aware that every time you update a value in the store, this will re-render every component that uses it, and this could create a huge performance problem!&lt;/p&gt;
&lt;h3&gt;
  
  
  Solutions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This one is the cleaner: use multiple contexts so when you update one only the components that are actually using values from that store will re-render&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;useMemo&lt;/code&gt; React hook, this way, you can specify, for every component using the global store, on which property change it will react.
I updated the previous example for this solution:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/9h4zw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I like the way it all blends in my code, it’s easy to manage, it does not need updates until I say so. It’s easy enough to rewrite if you are offline camping alone in a forest.&lt;/p&gt;

&lt;p&gt;Redux principles are still valid with this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Single source of truth&lt;/li&gt;
&lt;li&gt;✅ State is read-only&lt;/li&gt;
&lt;li&gt;✅ Changes are made with pure functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is true that Redux has more plugins and tools but at the end of the day I think that having a simple code in the first place is the best way to start off a new project and understand what happens when it runs.&lt;/p&gt;

&lt;p&gt;If you want a readymade, working solution for a global store using React hooks you can take a look to the following projects:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jhonnymichel"&gt;
        jhonnymichel
      &lt;/a&gt; / &lt;a href="https://github.com/jhonnymichel/react-hookstore"&gt;
        react-hookstore
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A state management library for react using the bleeding edge hooks feature 
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/forsigner"&gt;
        forsigner
      &lt;/a&gt; / &lt;a href="https://github.com/forsigner/stamen"&gt;
        stamen
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A React state management library based on Hooks
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ice-lab"&gt;
        ice-lab
      &lt;/a&gt; / &lt;a href="https://github.com/ice-lab/icestore"&gt;
        icestore
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      🌲 Simple and friendly state for React
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;🙏 Thanks to &lt;a href="https://t.me/full_stacks"&gt;Full-Staks Magazine&lt;/a&gt; for helping me out with downsides!&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;I’m &lt;a href="https://www.linkedin.com/in/alessandroannini"&gt;Alessandro Annini&lt;/a&gt;, I work at &lt;a href="https://scriby.it/"&gt;Scriby.it&lt;/a&gt;, my own company, offering software development and consulting services.&lt;/p&gt;

&lt;p&gt;Originally posted on Medium &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="https://medium.com/@alessandro.annini/replace-redux-with-react-hooks-the-easy-way-4ce9fe2cf231" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HbmWA2aW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/fit/c/56/56/2%2AIEeiyATAmoTSEYM3jIaqiQ.png" alt="Alessandro Annini"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://medium.com/@alessandro.annini/replace-redux-with-react-hooks-the-easy-way-4ce9fe2cf231" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Replace Redux with React Hooks, the easy way. | by Alessandro Annini | Medium&lt;/h2&gt;
      &lt;h3&gt;Alessandro Annini ・ &lt;time&gt;Apr 22, 2021&lt;/time&gt; ・ 
      &lt;div class="ltag__link__servicename"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hnDHPsJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/medium-f709f79cf29704f9f4c2a83f950b2964e95007a3e311b77f686915c71574fef2.svg" alt="Medium Logo"&gt;
        Medium
      &lt;/div&gt;
    &lt;/h3&gt;
&lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>react</category>
      <category>redux</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
