<?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: Richard McDaniel</title>
    <description>The latest articles on DEV Community by Richard McDaniel (@rmcdaniel).</description>
    <link>https://dev.to/rmcdaniel</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%2F961608%2Fd7bcf58a-f830-459d-bce3-98fb5d73041d.jpg</url>
      <title>DEV Community: Richard McDaniel</title>
      <link>https://dev.to/rmcdaniel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rmcdaniel"/>
    <language>en</language>
    <item>
      <title>From Idea to 1,000+ Stars: How Laravel Workflow Took Off</title>
      <dc:creator>Richard McDaniel</dc:creator>
      <pubDate>Mon, 28 Apr 2025 00:48:37 +0000</pubDate>
      <link>https://dev.to/rmcdaniel/from-idea-to-1000-stars-how-laravel-workflow-took-off-bb</link>
      <guid>https://dev.to/rmcdaniel/from-idea-to-1000-stars-how-laravel-workflow-took-off-bb</guid>
      <description>&lt;p&gt;I was working at a fintech company, wrestling with long-running processes that felt like a constant headache. We were using queued jobs that polled to check if an earlier step had completed, requeueing themselves if it hadn’t. It worked, but it was noisy, hard to track, and, honestly, felt wrong. I knew there had to be a better way.&lt;/p&gt;

&lt;p&gt;So, I went hunting for a solution.&lt;/p&gt;

&lt;p&gt;That’s when I stumbled across Temporal. Their PHP SDK was a breath of fresh air. You could write workflows like regular PHP code, yield async steps, and the system would magically resume where it left off. It was elegant, and the developer experience felt right. I was sold.&lt;/p&gt;

&lt;p&gt;Then I pitched it to the DevOps team.&lt;/p&gt;

&lt;p&gt;And hit a brick wall.&lt;/p&gt;

&lt;p&gt;They stared at me like I’d suggested launching a spaceship to run PHP jobs. Temporal required a Kubernetes cluster or a subscription to their cloud service. “Why would we add all this complexity just to replace Laravel queues?” they asked. I didn’t have a great answer. Deep down, I knew they had a point.&lt;/p&gt;

&lt;p&gt;We already had Laravel queues. We had a database. Couldn’t we build something similar without the ceremony?&lt;/p&gt;

&lt;p&gt;So, I rolled up my sleeves and got to work.&lt;/p&gt;

&lt;p&gt;Inspired by Temporal’s PHP SDK, I kept the core idea of using yield as a coroutine checkpoint but threw out the heavy stuff, no server, no cluster, no cloud.&lt;/p&gt;

&lt;p&gt;Now a workflow is just a queued job that runs until it hits a yield. When the yielded job completes, it requeues the workflow, rebuilds the state, and picks up where it left off.&lt;/p&gt;

&lt;p&gt;Here’s a quick example of how it looks in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class PaymentWorkflow extends Workflow
{
    public function execute($orderId)
    {
        $order = yield ActivityStub::make(ReserveOrder::class, $orderId);
        $payment = yield ActivityStub::make(ProcessPayment::class, $order-&amp;gt;id);
        yield ActivityStub::make(NotifyCustomer::class, $payment-&amp;gt;id);
        return $payment;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow reserves an order, processes a payment, and notifies the customer, with each step running asynchronously. If any step fails or needs retrying, the workflow resumes seamlessly. No polling, no mess.&lt;/p&gt;

&lt;p&gt;That was the proof of concept. It worked. We shipped a basic version to production, and it handled our workflows like a charm.&lt;/p&gt;

&lt;p&gt;But I couldn’t stop tinkering.&lt;/p&gt;

&lt;p&gt;It felt like a puzzle I had to solve. I added support for side effects, sagas, and child workflows, making it more powerful while keeping it dead simple.&lt;/p&gt;

&lt;p&gt;I shared the private repo with an engineer I’d befriended at Temporal (after pestering him with way too many questions). He took a look and said, “Wow, this is really cool. You should release it.”&lt;/p&gt;

&lt;p&gt;So, I did. I wrote docs, opened the &lt;a href="https://github.com/laravel-workflow/laravel-workflow" rel="noopener noreferrer"&gt;repo&lt;/a&gt;, and tagged a Composer release. I called it &lt;a href="https://laravel-workflow.com/" rel="noopener noreferrer"&gt;Laravel Workflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then something clicked with the community: Temporal isn’t built for PHP devs like us.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong. Temporal’s fantastic for massive, distributed systems at enterprise scale, like powering Netflix or Uber. But their PHP SDK, while solid, isn’t their priority. It even requires Roadrunner, an extra runtime on top of their server. That’s friction most Laravel devs don’t need.&lt;/p&gt;

&lt;p&gt;Meanwhile, Laravel Workflow is all about simplicity:&lt;/p&gt;

&lt;p&gt;No external services.&lt;/p&gt;

&lt;p&gt;No custom runtimes.&lt;/p&gt;

&lt;p&gt;Just Laravel queues, a database, and yield.&lt;/p&gt;

&lt;p&gt;Fast forward, and Laravel Workflow has quietly become the default for long-running orchestration in the Laravel community. It’s racked up over 1,000 stars on GitHub, climbed to the top of Google for terms like “Laravel workflow orchestration,” and even covers my domain fees through GitHub Sponsors.&lt;/p&gt;

&lt;p&gt;Temporal’s PHP SDK still has 4-5x more daily installs, but they’ve got $350 million in funding and a massive marketing machine. I’m just a guy with a repo and a passion for making PHP dev feel right.&lt;/p&gt;

&lt;p&gt;The only “downside”? I don’t have a shiny SaaS to sell. There’s no cloud product, no upsell. Laravel Workflow is just a tool that solves a problem, and I’m okay with that.&lt;/p&gt;

&lt;p&gt;But I’m not done yet. I’m dreaming up new features, exploring deeper Laravel integrations, and loving the community that’s rallying around this project.&lt;/p&gt;

&lt;p&gt;If you’re a Laravel dev struggling with long-running processes, give Laravel Workflow a try. Check out the GitHub repo, kick the tires, or share your own workflow war stories. Let’s keep building tools that make development fun, simple, and powerful.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>tooling</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Laravel Workflow: Orchestration vs. Choregraphy?</title>
      <dc:creator>Richard McDaniel</dc:creator>
      <pubDate>Tue, 01 Nov 2022 06:51:30 +0000</pubDate>
      <link>https://dev.to/rmcdaniel/laravel-workflow-orchestration-vs-choregraphy-5aj8</link>
      <guid>https://dev.to/rmcdaniel/laravel-workflow-orchestration-vs-choregraphy-5aj8</guid>
      <description>&lt;p&gt;As applications grow and evolve, even sending an email to a user can become a complex business process that spans days or weeks.&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%2F7dapv1b6rzv567nrjf4u.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%2F7dapv1b6rzv567nrjf4u.png" alt="email flow" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, many Laravel apps start as a monolith, evolve to a distributed monolith and then finally transition to lightweight microservices, highly decoupled and deployed independently.&lt;/p&gt;

&lt;p&gt;Microservices make it easier for Laravel apps to scale but they bring with them a new set of challenges. How do microservices coordinate with each other to complete a business process?&lt;/p&gt;

&lt;p&gt;The two commonly described solutions are orchestration and choreography.&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%2Fr9ypgh2nezvz8tph7jvh.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%2Fr9ypgh2nezvz8tph7jvh.png" alt="orchestration and choreography" width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With orchestration, the business process is captured explicitly in a single place. It can be easily monitored and retried if there is a failure. And each service is independent from each other. However, the downside is that the orchestrator tightly couples to each of the services and presents a single point of failure.&lt;/p&gt;

&lt;p&gt;With choreography, the services are loosely coupled and therefore more scalable, can change independently and are more fault tolerant. But the downside is that the logic for the business process, what we care most about, is now scattered throughout the system. Our business process has become an emergent property of the system, no longer explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Laravel Workflow
&lt;/h2&gt;

&lt;p&gt;Workflows combine the benefits of both of these. We can write our logic in a central orchestration but keep things loosely coupled and fault tolerant. I have written a package for this called &lt;a href="https://laravel-workflow.com" rel="noopener noreferrer"&gt;Laravel Workflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Laravel Workflow is powered by standard &lt;a href="https://laravel.com/docs/9.x/queues#class-structure" rel="noopener noreferrer"&gt;queued jobs&lt;/a&gt; and supports any queue library that Laravel does, Redis, SQS, etc. Under the hood, Laravel Workflow has two types of jobs, workflows and activities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Activities
&lt;/h3&gt;

&lt;p&gt;An activity is a fairly normal job. It has a few extra features to make it more fault tolerant. By default, activities will retry forever, until the transient error resolves itself, or someone deploys a code fix, restores a database table or fixes whatever is the root cause. But don't worry, activities have an exponential backoff. So you won't be hammering your queue while you frantically deploy a fix!&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Workflows\VerifyEmail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Mail\VerifyEmail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Mail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Workflow\Activity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SendEmailVerificationEmailActivity&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Activity&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="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VerifyEmail&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="nf"&gt;workflowId&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;Activities are what actually moves a workflow along. A workflow with a single activity is done after that activity is done. If that activity fails, the workflow will retry it until it succeeds.&lt;/p&gt;

&lt;p&gt;Activities are what actually change things. They send emails, rename files, update databases. They have side effects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflows
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Workflows\VerifyEmail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Workflow\ActivityStub&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Workflow\SignalMethod&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Workflow\Workflow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Workflow\WorkflowStub&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VerifyEmailWorkflow&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Workflow&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;bool&lt;/span&gt; &lt;span class="nv"&gt;$verified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="na"&gt;#[SignalMethod]&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;verify&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;verified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="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="nv"&gt;$email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&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;yield&lt;/span&gt; &lt;span class="nc"&gt;ActivityStub&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SendEmailVerificationEmailActivity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;WorkflowStub&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;verified&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;ActivityStub&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;VerifyEmailActivity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&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;Workflows are what capture the logic of the business process. They orchestrate activities. They start them, wait for them to finish, collect the results and based on the results conditionally call other activities. But they don't change anything. They don't have side effects other than running activities. They can be suspended and resumed. This is why it doesn't matter if you wait for a month inside of a workflow. It will just suspend itself for a month, not taking up any worker resources.&lt;/p&gt;

&lt;p&gt;Workflows capture the developer experience of orchestration but it is still based on choreography and all of the benefits that come with it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer Experience
&lt;/h2&gt;

&lt;p&gt;If you've made it this far, you might thinking, "How exactly is orchestration a better developer experience?" Okay, that's fair. Think about how you would have written the email verification above instead of using a workflow. Pretty simple right?&lt;/p&gt;

&lt;p&gt;Now imagine you receive updated requirements as follows, "We want a second email after the link in the first one expires. And if the second email expires, we want a third email apologizing and telling us that we need to register again."&lt;/p&gt;

&lt;p&gt;Here's a diff of all it would take to change the above workflow to meet those requirements.&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%2Fcemygns09pj73s24kscn.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%2Fcemygns09pj73s24kscn.png" alt="diff" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not only is it an easier change but all your logic is in a single place that makes it easier to reason about rather than scattered throughout the codebase in a bunch of different handlers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Digging Deeper
&lt;/h2&gt;

&lt;p&gt;Now if you're still skeptical you might be thinking, "Can't I already do this with Laravel queues?" And well, yes, in a very obvious sense because Laravel Workflow is written only using queues! You would just need to write similar code to what's in Laravel Workflow. But &lt;em&gt;what&lt;/em&gt; is in it?&lt;/p&gt;

&lt;p&gt;It is the same type of code that is in &lt;a href="https://aws.amazon.com/step-functions/" rel="noopener noreferrer"&gt;AWS Step Functions&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-overview?tabs=csharp" rel="noopener noreferrer"&gt;Azure Durable Functions&lt;/a&gt; and &lt;a href="https://temporal.io/" rel="noopener noreferrer"&gt;Temporal&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;These are all built on coroutines.&lt;/p&gt;

&lt;p&gt;In an ideal world, our workflows would never crash. In a slightly less than ideal world, if our workflow crashes, we could load it up, restore the exact execution state and retry it from where it crashed. We don't live in either of those worlds.&lt;/p&gt;

&lt;p&gt;Take notice of the &lt;code&gt;yield&lt;/code&gt; keywords from the workflow above. Because PHP (and most other languages) cannot save their execution state, coroutines rather than normal functions are used inside of workflows (but not activities). A coroutine will be called multiple times in order to execute to completion.&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%2Fhm7dws2nqzahzcabjjqj.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%2Fhm7dws2nqzahzcabjjqj.png" alt="coroutines" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even though the above workflow will execute to completion effectively once, it will still be partially executed &lt;em&gt;four different times&lt;/em&gt;. The results of activities are cached so that only failed activities will be called again. Successful activities get skipped.&lt;/p&gt;

&lt;p&gt;Let's walk through it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step By Step
&lt;/h3&gt;

&lt;p&gt;The first time the workflow executes, it will reach the call to &lt;code&gt;SendVerificationEmailActivity&lt;/code&gt;, start that activity, and then exit. Remember, workflows suspend execution while an activity is running. After the &lt;code&gt;SendVerificationEmailActivity&lt;/code&gt; finishes, it will resume execution of the workflow. This brings us to…&lt;/p&gt;

&lt;p&gt;The second time the workflow is executed, it will reach the call to &lt;code&gt;SendVerificationEmailActivity&lt;/code&gt; and skip it because it will already have the result of that activity. Then it will reach the call to &lt;code&gt;WorkflowStub::await()&lt;/code&gt; which allows the workflow to suspend itself and wait for an external signal. In this case, it will come from the user clicking on the verification link they receive in their email. Once the workflow is signaled then it will execute for…&lt;/p&gt;

&lt;p&gt;The third time, both the calls to &lt;code&gt;SendVerificationEmailActivity&lt;/code&gt; and &lt;code&gt;WorkflowStub::await()&lt;/code&gt; are skipped. This means that the &lt;code&gt;VerifyEmailActivity&lt;/code&gt; will be started. After the final activity has executed we still have…&lt;/p&gt;

&lt;p&gt;The final time the workflow is called, there is nothing left to do so the workflow completes. This final call allows the workflow to optionally combine the results from the previous activities.&lt;/p&gt;

&lt;p&gt;If we take a look at the output of &lt;code&gt;php artisan queue:work&lt;/code&gt; we can better see how the workflow and individual activities are interleaved.&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%2F5s2ddjiym2wiwm51tun9.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%2F5s2ddjiym2wiwm51tun9.png" alt="artisan" width="800" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Heartbeats
&lt;/h3&gt;

&lt;p&gt;Another great use case for workflows is any long running process such as converting video.&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Workflows\ConvertVideo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;FFMpeg\FFMpeg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;FFMpeg\Format\Video\WebM&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Workflow\Activity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConvertVideoWebmActivity&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Activity&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$ffmpeg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FFMpeg&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ffmpeg&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebM&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$format&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'progress'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nf"&gt;heartbeat&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$output&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;Because activities support heartbeats unlike normal Laravel jobs, we can set a reasonable timeout of 5 seconds but let ffmpeg run for as long as it needs.&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%2Fx34a99jo7n2e3ugbxcss.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%2Fx34a99jo7n2e3ugbxcss.png" alt="yes" width="800" height="67"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;vs.&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%2Fka25p452z25xdbg2jejd.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%2Fka25p452z25xdbg2jejd.png" alt="no" width="800" height="40"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're &lt;em&gt;still&lt;/em&gt; interested in learning more about workflows outside of the Laravel package that I wrote, I highly recommend this video from Temporal.&lt;/p&gt;

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

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
      <category>laravel</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
