<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Andrii</title>
    <description>The latest articles on Forem by Andrii (@andriimz).</description>
    <link>https://forem.com/andriimz</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%2F1280002%2Fe5353b82-7b2d-4938-a6d4-c80ca9b92a9c.jpeg</url>
      <title>Forem: Andrii</title>
      <link>https://forem.com/andriimz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/andriimz"/>
    <language>en</language>
    <item>
      <title>Problem #1/100: Why your "clean" codebase still takes two weeks to change one thing</title>
      <dc:creator>Andrii</dc:creator>
      <pubDate>Tue, 21 Apr 2026 11:08:18 +0000</pubDate>
      <link>https://forem.com/andriimz/problem-1100-why-your-clean-codebase-still-takes-two-weeks-to-change-one-thing-43na</link>
      <guid>https://forem.com/andriimz/problem-1100-why-your-clean-codebase-still-takes-two-weeks-to-change-one-thing-43na</guid>
      <description>&lt;p&gt;You've refactored the fat controllers. You moved logic into services. Your classes have single responsibilities, your methods are short, you use enums and value objects. The code looks clean.&lt;/p&gt;

&lt;p&gt;And yet — every time the product owner asks to change how failed actions are retried, your team touches twelve files. Every time you swap an external provider, the estimate is two weeks. Every time someone asks "can we add logging here?", the answer involves a pull request across six services.&lt;/p&gt;

&lt;p&gt;The codebase looks clean. The architecture is still tangled.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Business logic must not know what infrastructure surrounds it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your domain actions — the things your product actually &lt;em&gt;does&lt;/em&gt; — should declare &lt;em&gt;what happened&lt;/em&gt;. Infrastructure decides &lt;em&gt;what to do about it&lt;/em&gt;. When domain actions also handle logging, failure persistence, retry scheduling, and notifications, you don't have separation. You have infrastructure wearing a service-class costume.&lt;/p&gt;

&lt;h2&gt;
  
  
  The example that passes code review
&lt;/h2&gt;

&lt;p&gt;A workflow automation system. Multiple step types - send an email, generate a document, update a CRM record. Each step can fail, and failures need to be logged to a tracking table, recorded as a note on the deal, and retried.&lt;/p&gt;

&lt;p&gt;Here's what the code looks like after a "clean up" refactor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step A — send automated email&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendAutomatedEmail&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;NoteService&lt;/span&gt; &lt;span class="nv"&gt;$noteService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;StepFailureRepository&lt;/span&gt; &lt;span class="nv"&gt;$failureRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;MailerInterface&lt;/span&gt; &lt;span class="nv"&gt;$mailer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Workflow&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Deal&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mailer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;templateId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasBody&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;noteService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addWorkflowNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;noteType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NoteType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EMAIL_STEP_FAILED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Email body rendered empty.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;failureRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;errorDetails&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'code'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;StepErrorCode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;EMPTY_BODY&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Email body rendered empty.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mailer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;contactEmail&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step B — generate proposal document&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GenerateProposal&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;NoteService&lt;/span&gt; &lt;span class="nv"&gt;$noteService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;StepFailureRepository&lt;/span&gt; &lt;span class="nv"&gt;$failureRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;DocumentGenerator&lt;/span&gt; &lt;span class="nv"&gt;$generator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Workflow&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Deal&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;proposalConfig&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;noteService&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addWorkflowNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;noteType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NoteType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PROPOSAL_STEP_FAILED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Document generation failed.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;failureRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;workflow&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;errorDetails&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'code'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;StepErrorCode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;GENERATION_FAILED&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Document generation failed.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code passes review. Named arguments, enums for error codes, a dedicated repository, a note service. It looks like someone who read the books.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But it has the same structural problem as a fat controller.&lt;/strong&gt; Every step repeats the same three-part failure ritual: log a note → persist a failure record → throw. The ritual is infrastructure - the business doesn't care &lt;em&gt;how&lt;/em&gt; failures are tracked. The business cares that the email was empty or the document didn't generate. Everything after that is plumbing.&lt;/p&gt;

&lt;p&gt;Now add a retry mechanism. You touch every step. Change the failure table schema. Every step. Add a Slack notification on failure. Every step. The "clean" code has the same change-propagation cost as the spaghetti it replaced.&lt;/p&gt;

&lt;h3&gt;
  
  
  After
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step A — declares what went wrong, nothing else&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendAutomatedEmail&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;MailerInterface&lt;/span&gt; &lt;span class="nv"&gt;$mailer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Workflow&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Deal&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mailer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;templateId&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasBody&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;WorkflowStepFailed&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;because&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;currentStep&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FailureReason&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;EmptyEmailBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mailer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;contactEmail&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step B — same shape, different reason&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GenerateProposal&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;DocumentGenerator&lt;/span&gt; &lt;span class="nv"&gt;$generator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Workflow&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Deal&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;proposalConfig&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;WorkflowStepFailed&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;because&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;currentStep&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FailureReason&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;DocumentGenerationFailed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Workflow runner — one place owns all failure infrastructure&lt;/span&gt;
&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WorkflowRunner&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;NoteService&lt;/span&gt; &lt;span class="nv"&gt;$notes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;StepFailureRepository&lt;/span&gt; &lt;span class="nv"&gt;$failures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;RetryScheduler&lt;/span&gt; &lt;span class="nv"&gt;$retries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;WorkflowStep&lt;/span&gt; &lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Workflow&lt;/span&gt; &lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Deal&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WorkflowStepFailed&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addWorkflowNote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$deal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;details&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;retries&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$workflow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$step&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What changed:&lt;/strong&gt; Steps only declare domain facts — "the email body was empty." The runner decides what to do about it. Add retry? One file. Add Slack alert? One file. Change failure schema? One file. Steps never change for infrastructure reasons again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test
&lt;/h2&gt;

&lt;p&gt;Pick a service class that "looks clean." Count its constructor dependencies. How many of those are infrastructure (repositories, loggers, notifiers, queue dispatchers) vs. domain (other business rules, value objects, domain services)? If infrastructure dependencies outnumber domain ones, the class is doing two jobs — it just doesn't look like it.&lt;/p&gt;




&lt;p&gt;Which non-obvious case should be Problem #2? Drop the symptom in the comments — I pick the next article from what developers actually hit in the wild.&lt;/p&gt;

&lt;p&gt;I help SaaS founders untangle these exact problems (and stop paying the compound interest on invisible architectural debt) at &lt;a href="https://mvpforstartup.com" rel="noopener noreferrer"&gt;mvpforstartup.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>saas</category>
      <category>mvp</category>
      <category>startup</category>
    </item>
    <item>
      <title>Why Git Is the Ultimate Tool for Managing AI Prompts (And What You’re Missing)</title>
      <dc:creator>Andrii</dc:creator>
      <pubDate>Fri, 04 Apr 2025 14:26:00 +0000</pubDate>
      <link>https://forem.com/andriimz/why-git-is-the-ultimate-tool-for-managing-ai-prompts-and-what-youre-missing-158l</link>
      <guid>https://forem.com/andriimz/why-git-is-the-ultimate-tool-for-managing-ai-prompts-and-what-youre-missing-158l</guid>
      <description>&lt;p&gt;If you're storing AI prompts in a database or hardcoding them into your code, you're probably making your life a lot harder than it needs to be. In this article, we’ll show you why &lt;strong&gt;Git&lt;/strong&gt; is the superior method for managing AI prompts, offering &lt;strong&gt;version control&lt;/strong&gt;, &lt;strong&gt;privacy&lt;/strong&gt;, and &lt;strong&gt;collaboration&lt;/strong&gt; at no cost.&lt;/p&gt;

&lt;p&gt;Using Git for prompt management is the key to unlocking faster, more efficient workflows and eliminating the common pitfalls of API-based tools. Plus, with libraries like &lt;strong&gt;GptSdk&lt;/strong&gt;, you can fetch and test prompts directly from Git, streamlining the entire process. Don’t miss out on the future of prompt management!&lt;/p&gt;

&lt;p&gt;Link: &lt;a href="https://gpt-sdk.com/c/Version-Control-Best-Practices-for-Prompt-Management" rel="noopener noreferrer"&gt;Learn More About Managing AI Prompts with Git&lt;/a&gt; &lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Secret to Faster, More Efficient AI Prompt Management – You Won’t Believe What These Tools Can Do!</title>
      <dc:creator>Andrii</dc:creator>
      <pubDate>Tue, 18 Mar 2025 17:00:00 +0000</pubDate>
      <link>https://forem.com/andriimz/the-secret-to-faster-more-efficient-ai-prompt-management-you-wont-believe-what-these-tools-can-472d</link>
      <guid>https://forem.com/andriimz/the-secret-to-faster-more-efficient-ai-prompt-management-you-wont-believe-what-these-tools-can-472d</guid>
      <description>&lt;p&gt;Managing AI prompts is crucial for developers, but it’s easy to get stuck with tools that slow you down or lock your prompts into proprietary ecosystems. In our latest comparison, we dive deep into three popular AI prompt managers — &lt;strong&gt;PromptHub&lt;/strong&gt;, &lt;strong&gt;Humanloop&lt;/strong&gt;, and &lt;strong&gt;GptSdk&lt;/strong&gt; — to reveal which one truly offers &lt;strong&gt;speed&lt;/strong&gt;, &lt;strong&gt;control&lt;/strong&gt;, and &lt;strong&gt;data security&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Whether you’re dealing with latency, privacy risks, or lack of flexibility, you’ll want to check out which tool can &lt;strong&gt;boost your productivity&lt;/strong&gt; and simplify your workflow. Curious to see which tool suits your needs? Click the link below to read the full comparison!&lt;/p&gt;

&lt;p&gt;Link: &lt;a href="https://gpt-sdk.com/c/Comparing-AI-Prompt-Management-Tools" rel="noopener noreferrer"&gt;Read the Full Article on AI Prompt Management&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>Laravel/Symfony Upgrade like a PRO 🦄</title>
      <dc:creator>Andrii</dc:creator>
      <pubDate>Wed, 27 Mar 2024 17:20:44 +0000</pubDate>
      <link>https://forem.com/andriimz/laravelsymfony-upgrade-like-a-pro-i22</link>
      <guid>https://forem.com/andriimz/laravelsymfony-upgrade-like-a-pro-i22</guid>
      <description>&lt;p&gt;Hello &lt;a href="http://dev.to/"&gt;dev.to&lt;/a&gt; community 👋&lt;/p&gt;

&lt;p&gt;Today, I spent the entire day migrating a huge project from version 6.3 to 6.4 on the Symfony framework. I found myself wondering if there was a way to make it easier. Is there a method to automatically check for potential issues that could potentially disrupt the project?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there a tool that can potentially help overcome these problems?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;I suppose the best way to address this is to cover the project with unit tests. However, in real-life projects, there are often challenges such as pressure from business stakeholders and MVP mindsets that force developers to ignore duties related to unit tests.&lt;/p&gt;

&lt;p&gt;If there are people in the &lt;a href="http://dev.to/"&gt;dev.to&lt;/a&gt; community with experience in automated package upgrades, please share your experiences in the comments.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>symfony</category>
      <category>php</category>
    </item>
    <item>
      <title>🏂 Use Anthropic Claude in your Laravel application today 💫</title>
      <dc:creator>Andrii</dc:creator>
      <pubDate>Mon, 11 Mar 2024 16:56:29 +0000</pubDate>
      <link>https://forem.com/andriimz/use-anthropic-claude-in-your-laravel-application-today-46k8</link>
      <guid>https://forem.com/andriimz/use-anthropic-claude-in-your-laravel-application-today-46k8</guid>
      <description>&lt;p&gt;Good News 📰 &lt;/p&gt;

&lt;p&gt;Starting today, GptSdk allows you to run prompts using Anthropic Claude. Anthropic gives $5 when you create a new account for testing, so you can jump in and test whether there are any pros for your application.&lt;/p&gt;

&lt;p&gt;If you already have an OpenAI prompt running with GptSdk, all you need to do is switch the connector for the prompt from the user interface.&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%2Fqdd2jedei6lfkb2wvi34.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%2Fqdd2jedei6lfkb2wvi34.png" alt="GptSdk Connector Settings" width="800" height="356"&gt;&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xdqlaqtcpc8a48v1u9h.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%2F2xdqlaqtcpc8a48v1u9h.png" alt="GptSdk Connector Settings" width="779" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your prompt is still hardcoded 🤷, don't give up. Install the &lt;a href="https://packagist.org/packages/growthapps/gptsdk" rel="noopener noreferrer"&gt;GptSdk library&lt;/a&gt; for free now and switch your hardcoded prompt to the Anthropic AI vendor.&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%2Fo054eh4fnkbz7yaeu1ej.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%2Fo054eh4fnkbz7yaeu1ej.png" alt="GptSdk library example" width="800" height="943"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now is a great time to switch to GPTSDK📈, as it allows you to switch models on the fly🪁, especially when there are new AI announcements from market players.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>laravel</category>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to create a valuable prompt for Open Ai 🤯</title>
      <dc:creator>Andrii</dc:creator>
      <pubDate>Sat, 02 Mar 2024 17:51:36 +0000</pubDate>
      <link>https://forem.com/andriimz/how-to-create-a-valuable-prompt-for-open-ai-j9c</link>
      <guid>https://forem.com/andriimz/how-to-create-a-valuable-prompt-for-open-ai-j9c</guid>
      <description>&lt;p&gt;Hey dev.to community 👋&lt;/p&gt;

&lt;p&gt;Today, I would like to inform you about a highly valuable opportunity that can help save your time when creating a prompt.&lt;/p&gt;

&lt;p&gt;The main issue with dynamic prompts is that the results are not repeatable. Obtaining a cool result for one set of parameters does not guarantee the same outcome for another set of parameters 🤯. Manually testing a large dataset can be a bit tricky🏓; you have to create loops, manage timeouts, and deal with other issues. However, now you can test your prompt in just a few clicks.&lt;/p&gt;

&lt;p&gt;Try GptSdk's &lt;strong&gt;Bulk Run&lt;/strong&gt; feature and test your prompt with large datasets in minutes 🚀. Simply create a CSV file with input data, upload it to GptSdk, match fields with prompt parameters, and enjoy your coffee☕️ while GptSdk handles the job.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwuf7s516fzmeo4sg8m3c.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%2Fwuf7s516fzmeo4sg8m3c.png" alt="Upload Csv File" width="800" height="369"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p4ds8nt780xrrg0tk4u.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%2F1p4ds8nt780xrrg0tk4u.png" alt="Match prompt dynamic parameters with CSV fields" width="800" height="365"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiiy6998ykbmyf196v6qc.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%2Fiiy6998ykbmyf196v6qc.png" alt="Analyze Prompt Results" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the bulk run, you can download the results in a CSV file or compare them in GptSdk. Choose the best results for fine-tuning your model and achieve a more reliable prompt.&lt;/p&gt;

&lt;p&gt;Create a free &lt;a href="https://gpt-sdk.com" rel="noopener noreferrer"&gt;GptSdk&lt;/a&gt; account now and stop worrying about prompt management.&lt;/p&gt;

&lt;p&gt;If you have any questions about how it works and how it can be helpful, I am open to discussing them in the comments 🙏.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>openai</category>
      <category>promptengineering</category>
      <category>php</category>
    </item>
    <item>
      <title>🚨OpenAi secret you should know 🚨</title>
      <dc:creator>Andrii</dc:creator>
      <pubDate>Fri, 23 Feb 2024 17:40:31 +0000</pubDate>
      <link>https://forem.com/andriimz/openai-secret-you-should-know-c6f</link>
      <guid>https://forem.com/andriimz/openai-secret-you-should-know-c6f</guid>
      <description>&lt;p&gt;I think almost everyone in the dev.to community has given OpenAI prompts a shot in the past year.😂&lt;/p&gt;

&lt;p&gt;The first time I made a prompt, I was excited to see the results. After a few tries, I got some ideas and thought about creating a few AI products.🚀&lt;/p&gt;

&lt;p&gt;The first tool I tried to build was for keeping an eye on crypto news 💸. It was a simple idea: analyze RSS feeds, collect content, and send it to AI for summarization. To get useful insights for crypto investments, we needed to analyze a lot of information. The best way to understand the data was to make charts. But to make a chart, we needed metrics. So, we decided to create a prompt with JSON output containing metrics. My metric was simple: a scale from 1 to 5, showing whether to buy a coin or not (1 means "should not buy," and 5 means "all in").&lt;/p&gt;

&lt;p&gt;We launched the first version of the tool, looked at cryptonews for two weeks, and saw that the chart was completely empty. I checked a few log records and found out the results had invalid JSON. That disappointment led to a good idea that has helped me make many successful JSON prompts. In the System section, make sure to tell the AI that it's a RESTful JSON API and give an example of the output. You can see my example in the screenshots below.&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%2Fryfu4e1z94ihzkkbf8d5.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%2Fryfu4e1z94ihzkkbf8d5.png" alt=" " width="800" height="353"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrx1d6k4j3k8hclr3k77.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%2Fgrx1d6k4j3k8hclr3k77.png" alt=" " width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After these updates, the results were really good. I finally got a useful chart. So, the main trick with OpenAI is to use it like a RESTful JSON API.&lt;/p&gt;

&lt;p&gt;Give it a try yourself with my example at &lt;a href="https://gpt-sdk.com" rel="noopener noreferrer"&gt;https://gpt-sdk.com&lt;/a&gt;. Just create a free account and explore what you can do.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>🕵 What You Didn't Know About AI Integration for Laravel Applications</title>
      <dc:creator>Andrii</dc:creator>
      <pubDate>Fri, 16 Feb 2024 17:26:29 +0000</pubDate>
      <link>https://forem.com/andriimz/what-you-didnt-know-about-ai-integration-for-laravel-applications-2j44</link>
      <guid>https://forem.com/andriimz/what-you-didnt-know-about-ai-integration-for-laravel-applications-2j44</guid>
      <description>&lt;p&gt;🚨 Great news for Laravel developers! &lt;/p&gt;

&lt;p&gt;We're working on a package called GptSdk that allows you to build AI features in Laravel applications in minutes.&lt;/p&gt;

&lt;p&gt;What we know about AI features? In general, it should be a prompt and something that transforms prompt output into user-friendly results. That's all. But for every successful project, there are huge backend tasks: continuous updates of business logic, new requirements, and adapting to new market trends. Hard-coded prompts can quickly become a pain. 💩&lt;/p&gt;

&lt;p&gt;If you've encountered this problem or have decided to embark on your AI journey, consider trying the GptSdk package, available at &lt;a href="https://github.com/growthapps/gptsdk" rel="noopener noreferrer"&gt;https://github.com/growthapps/gptsdk&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;All you need to add AI features to your app today is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the library.&lt;/li&gt;
&lt;li&gt;Create a free GptSdk account at &lt;a href="https://gpt-sdk.com" rel="noopener noreferrer"&gt;https://gpt-sdk.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Define your prompt.&lt;/li&gt;
&lt;li&gt;Add a small snippet to your codebase:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$gptSdkClient = new GptSdkApiClient(
    HttpClient::create(),
    '&amp;lt;your api key here&amp;gt;'
);
$promptRun = $gptSdkClient-&amp;gt;runPrompt(
    new PromptRun(
        vendorKey: VendorEnum::OPENAI,
        promptMessages: new ArrayCollection(
            [
                new PromptMessage(
                    role: 'User',
                    content: 'Hello gpt! How are you? Reply in [[tone]] tone.'
                )
            ]
        ),
        promptKey: 'hello_prompt',
        params: new ArrayCollection(
            [
                new PromptParam(
                    type: Type::STRING,
                    key: 'tone',
                    value: 'angry'
                )
            ]
        ),
    )
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all! The next step is to enjoy AI integration and move forward in building great applications. 🦄&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>openai</category>
      <category>ai</category>
      <category>php</category>
    </item>
  </channel>
</rss>
