Genkit flows make AI work typed and testable; multi-step pipelines still need workers, retries, progress tracking, and clear logs.

Flows Are the Building Blocks
Genkit flows give Go developers typed, traced, testable units of AI work that can be called from different parts of an application. That structure helps make an individual task easier to understand: a flow has defined inputs and outputs, can be exercised in tests, and can leave traces that help explain what happened during execution. For a single request, that may be enough. The difficulty changes when a task becomes a sequence or a batch of work, because the application must then decide how those flows interact and what happens when execution doesn't go as planned.
Consider the examples in the source: processing twelve summaries, passing one flow’s output to two others, or recovering when a model call fails partway through forty items. Each example adds a coordination problem. The application has to manage ordering, concurrency, dependencies, partial completion, and errors. A flow can define a unit of work, but that doesn't automatically determine how a collection of units should be scheduled or recovered.
The Coordination Gap
Processing a batch sounds straightforward until the work takes long enough for failures to matter. A program might need to divide items among workers, keep the number of simultaneous calls within acceptable bounds, and collect results in a form the next step can use. If tasks have different runtimes, workers may finish at different times. If results need to preserve the input order, the program must track that relationship instead of simply appending outputs as they arrive.
Dependency adds another layer. When one flow’s output feeds two others, the system needs to know which steps can run independently and which must wait for earlier results. Running independent work at the same time can reduce waiting, but it also means failures and completion events can arrive in an order the developer didn't anticipate. The orchestration layer has to represent those relationships clearly enough that the behavior remains understandable during normal operation and when something breaks.
Here's the thing: typed inputs and outputs can make the boundaries between flows clearer, but they don't by themselves provide a policy for coordinating those flows. Developers still need to choose how work is divided, when a failed step is retried, and which results count as complete. Those choices shape the behavior of the whole pipeline.
Retries and Partial Completion
A model call can fail after some items in a batch have already succeeded. Retrying the entire batch may repeat completed work, while retrying only the failed item requires the application to know which item failed and whether its earlier steps produced usable results. The source highlights this problem directly: tracking completed items can prevent unnecessary reruns, especially when a failure happens partway through a larger set.
That tracking needs a reliable definition of “complete.” Does an item count as finished when its first flow succeeds, or only after every dependent flow has produced an output? If a downstream step fails, the system may need to preserve the earlier result and resume from the point of failure. If it can't distinguish partial progress from a finished item, retries can create duplicated work or leave the pipeline with gaps that are hard to spot.
Retries also need limits and context. A transient problem may clear on another attempt; a persistent problem may simply consume time and resources if the system keeps repeating the same call. The source doesn't prescribe a retry policy, and no single policy fits every workflow. The important point is that the policy belongs in the operational design, where developers can make its effects visible and test how the pipeline responds.
Worker Pools and Operational Visibility
Worker pools offer one way to control how many pieces of work run at once. Without a limit, a batch can produce more concurrent activity than the surrounding application or service can handle comfortably. With a limit that's too low, processing may be slower than necessary. Developers therefore need to consider the work the flows perform, the resources available to the application, and what a useful level of concurrency looks like for that particular task.
Concurrency makes logs and traces more important, not less. When many items are moving through a pipeline at once, a message that says only “flow failed” isn't enough to explain what happened. Developers need a way to connect the failure to the relevant item and step, see which work had already completed, and understand whether a retry occurred. Otherwise, diagnosing a problem becomes a reconstruction exercise based on scattered output.
And this is the part most people overlook: the person investigating a failure may not be watching the system when it happens. The source calls out the three-in-the-morning scenario for a reason. Operational information has to remain useful after the original context has faded, with enough detail to distinguish an isolated failed item from a broader problem in the pipeline.
Logs and traces serve related but different purposes. Logs can record events and decisions, while traces can help show how a particular piece of work moved through connected steps. Neither one automatically supplies good diagnosis. The application still needs to attach meaningful context to its records and avoid making a developer infer the pipeline’s state from a long, unstructured stream of messages.
Testing the Pipeline, Not Just the Flow
Testing an individual flow can show whether that unit handles its inputs and produces expected outputs. Orchestration introduces separate questions: does the batch continue when one item fails, does it avoid repeating completed work, and do dependent steps run only after their inputs are ready? Those questions concern the behavior around the flow, so they need attention at the pipeline level as well.
Tests can help make those expectations explicit. A developer might check how the application records progress, how it handles a failed call, or whether results remain associated with their original items when work finishes out of order. These are general concerns for systems that coordinate multiple units of work; the particular checks depend on the pipeline’s design.
There’s a practical payoff. When failure behavior is tested alongside successful execution, developers have a clearer basis for changing the system later. Without that coverage, a small change to scheduling or retry handling can alter how partial results are treated, with consequences that may appear only under failure conditions.
Why the Operational Layer Matters
The central point is that a well-defined flow and a dependable pipeline solve different parts of the problem. Flows organize AI work into typed, traced, testable units. Orchestration determines how those units are combined, scheduled, monitored, and resumed. The article’s examples are modest in size, but they expose the same kinds of questions that arise whenever work is split across items or connected through multiple steps.
This is more significant than it looks. A pipeline can produce correct results when every call succeeds and still be difficult to operate when one call fails midway through a batch. Reliability depends on knowing what finished, what didn't, and what the system will do next. A failure that can be identified and recovered from is easier to manage than one that leaves the application’s state ambiguous.
If you're working in this space, treat worker management, retry behavior, completion tracking, and diagnostic logs as part of the pipeline design from the start. They determine whether individual flows remain useful when combined into larger tasks. Typed and testable units are a strong foundation. But the handoffs between them, and the system’s account of partial progress, are where much of the operational work begins.
Clear boundaries. Visible progress. A recovery path.
Discussion
Sign in to join the discussion.