Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
156693 stories
·
33 followers

Blue/green deployments on Kubernetes with Argo Rollouts

1 Share

One of the harder questions to answer at scale is how to ship without a few seconds where your users are getting timeouts or your fleet is split across two image versions.

Kubernetes’s default rolling update strategy gradually deploys new pods and retires old ones, but during the swap, your service runs both versions side by side, and a regression in the new image affects every request that lands on a new pod.

Progressive delivery patterns like blue/green have long existed: you stand the new version up alongside the old, prove it’s healthy on a separate preview endpoint, then flip user traffic across. Blast radius shrinks to nothing in the bad case, rollback is a single command, and your release stops being a held-breath moment.

In this post, you’ll set that up with Argo Rollouts, the controller behind progressive delivery in the Argo ecosystem (which graduated from the CNCF in 2022).

What is Argo Rollouts (and why you need it)

Argo Rollouts is a Kubernetes controller and a set of CRDs that bolt blue/green, canary, and other progressive delivery strategies onto your cluster. The primary CRD is Rollout, a drop-in replacement for the standard Deployment.

You convert an existing Deployment by changing the apiVersion to argoproj.io/v1alpha1 and the kind to Rollout, then adding a strategy.blueGreen or strategy.canary block that describes how a new revision should roll out.

A major reason to reach for it is that there’s no built-in way to do this kind of traffic control in Kubernetes (you can’t decide where requests go independently of which pods are Ready). There’s no easy rollback to the previous version once the update has started.

Argo Rollouts fills in everything around that. It plugs into ingress controllers (Traefik, ALB) and service meshes (Istio, Linkerd, SMI) for real traffic shaping. It can query metrics providers (Prometheus, Datadog, CloudWatch, New Relic) to gate promotions on hard numbers, and it tracks every revision as its own ReplicaSet, so flipping back is instant.

Prerequisites

This tutorial assumes some familiarity with Kubernetes. You’ll also need:

  • A working Kubernetes cluster (EKS, GKE, AKS, or local like Minikube/Kind).
  • kubectl installed and pointed at the cluster (kubectl get nodes should return at least one Ready node)
  • helm v3 installed
  • curl for hitting the demo app

Step 1: Installing the Argo Rollouts controller

Argo Rollouts ships as a controller that runs in its own namespace, along with a kubectl plugin you’ll use on your laptop to inspect and steer rollouts.

To install the controller, follow these steps:

  1. Add the Argo Helm repo and install the controller:

    helm repo add argo https://argoproj.github.io/argo-helm
    helm repo update
    helm install argo-rollouts argo/argo-rollouts \
      --namespace argo-rollouts \
      --create-namespace \
      --wait
  2. Confirm the controller pods are up:

    kubectl -n argo-rollouts get pods

    You should see something like:

    NAME                             READY   STATUS    RESTARTS   AGE  
    argo-rollouts-dcd465dfc-8m2ql    1/1     Running   0          79s  
    argo-rollouts-dcd465dfc-q92k4    1/1     Running   0          79s

Step 2: Installing the kubectl argo rollouts plugin

The controller is running, but the most ergonomic way to drive a rollout — inspecting state, setting images, promoting, rolling back — is the kubectl argo rollouts plugin. It’s a separate binary that drops onto your PATH, and kubectl picks it up automatically.

To install the plugin, follow these steps:

On macOS with Homebrew:

brew install argoproj/tap/kubectl-argo-rollouts

On Linux:

curl -sLO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts

Verify it’s wired up:

kubectl argo rollouts version

You should see something like kubectl-argo-rollouts: v1.8.3+.... From here on, we’ll use kubectl argo rollouts … subcommands to drive the rollout.

Step 3: Defining the Rollout

Argo Rollouts’ core idea is that you swap your Deployment for a Rollout resource. The pod template inside it is identical to a Deployment’s.

What changes is the spec.strategy block, which describes how a new revision should roll out.

For blue/green, you need three things:

  1. A Rollout with spec.strategy.blueGreen configured
  2. An active Service, which always points to whichever ReplicaSet is currently serving production traffic
  3. A preview Service, which points at the new ReplicaSet before it gets promoted, so that you can test it in isolation

Argo Rollouts injects the rollouts-pod-template-hash label into each Service’s selector at runtime, which is how it switches traffic without you ever editing the Services.

Write the manifest:


cat > rollout.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo-active
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
  selector:
    app: rollouts-demo
---
apiVersion: v1
kind: Service
metadata:
  name: rollouts-demo-preview
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
  selector:
    app: rollouts-demo
---
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: rollouts-demo
spec:
  replicas: 2
  revisionHistoryLimit: 2
  selector:
    matchLabels:
      app: rollouts-demo
  template:
    metadata:
      labels:
        app: rollouts-demo
    spec:
      containers:
        - name: rollouts-demo
          image: argoproj/rollouts-demo:blue
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          resources:
            requests:
              cpu: 25m
              memory: 32Mi
  strategy:
    blueGreen:
      activeService: rollouts-demo-active
      previewService: rollouts-demo-preview
      autoPromotionEnabled: false
      scaleDownDelaySeconds: 30
EOF

A few fields are worth calling out in the blueGreen block:

  • activeService and previewService are the names of the two ClusterIP Services above. Argo Rollouts owns their selectors from here on; you don’t edit them by hand.
  • autoPromotionEnabled: false is what makes this a manual promotion. The new ReplicaSet comes up, you inspect it on the preview Service, and only then do you flip the active Service over. Set it to true (the default) and Argo will auto-promote the moment the new pods are Ready.
  • scaleDownDelaySeconds: 30 keeps the old (blue) ReplicaSet around for 30 seconds after promotion, so if something goes wrong in those first few seconds, you can flip back instantly without rescheduling pods.

Apply it:

kubectl apply -f rollout.yaml

We’re using argoproj/rollouts-demo,, a tiny app published by the Argo team that serves an HTML dashboard and a /color endpoint that reports which tagged image is running (blue, green, yellow, etc.). It’s perfect for seeing the cutover happen in real time.

Step 4: Checking the initial state

Take a look at the rollout:

kubectl argo rollouts get rollout rollouts-demo

Output:

Name:            rollouts-demo  
Namespace:       default  
Status:          ✔ Healthy  
Strategy:        BlueGreen  
Images:          argoproj/rollouts-demo:blue (stable, active)  
Replicas:  
  Desired:       2  
  Current:       2  
  Updated:       2  
  Ready:         2  
  Available:     2  

NAME                                       KIND        STATUS     AGE  INFO  
⟳ rollouts-demo                            Rollout     ✔ Healthy  34s  
└──\# revision:1  
   └──⧉ rollouts-demo-86c957c6d6           ReplicaSet  ✔ Healthy  34s  stable,active  
      ├──□ rollouts-demo-86c957c6d6-72kjf  Pod         ✔ Running  34s  ready:1/1  
      └──□ rollouts-demo-86c957c6d6-nv3zg  Pod         ✔ Running  34s  ready:1/1

One revision, two pods, both stable and active. Both Services currently point at the same ReplicaSet hash. You can confirm with:

kubectl get svc rollouts-demo-active rollouts-demo-preview \
  -o jsonpath='{range .items[*]}{.metadata.name}{" -> hash="}{.spec.selector.rollouts-pod-template-hash}{"\n"}{end}'

Output:

rollouts-demo-active -> hash=86c957c6d6
rollouts-demo-preview -> hash=86c957c6d6

Step 5: Triggering a new version

Now let’s deploy a new revision. We’ll change the image tag from blue to yellow:

kubectl argo rollouts set image rollouts-demo \
  rollouts-demo=argoproj/rollouts-demo:yellow

Argo creates a new ReplicaSet (rev 2) for the yellow image and waits, because we set autoPromotionEnabled: false. The active Service still points at blue. The preview Service is re-pointed at yellow:

kubectl argo rollouts get rollout rollouts-demo

Output:

Status:          ॥ Paused
Message:         BlueGreenPause
Strategy:        BlueGreen
Images:          argoproj/rollouts-demo:blue (stable, active)
                 argoproj/rollouts-demo:yellow (preview)
Replicas:
  Desired:       2
  Current:       4
  Updated:       2
  Ready:         2
  Available:     2

NAME                                       KIND        STATUS     AGE  INFO
⟳ rollouts-demo                            Rollout     ॥ Paused
├──# revision:2
│  └──⧉ rollouts-demo-7cf9dff6bb           ReplicaSet  ✔ Healthy  38s  preview
│     ├──□ rollouts-demo-7cf9dff6bb-cbp2c  Pod         ✔ Running  38s  ready:1/1
│     └──□ rollouts-demo-7cf9dff6bb-fn2gt  Pod         ✔ Running  38s  ready:1/1
└──# revision:1
   └──⧉ rollouts-demo-86c957c6d6           ReplicaSet  ✔ Healthy  5m   stable,active
      ├──□ rollouts-demo-86c957c6d6-72kjf  Pod         ✔ Running  5m   ready:1/1
      └──□ rollouts-demo-86c957c6d6-nv3zg  Pod         ✔ Running  5m   ready:1/1

This is the heart of blue/green. The cluster is now running both versions, but only blue is serving real traffic.

Step 6: Proving the split with curl

Forward both Services to your laptop on different local ports:

kubectl port-forward svc/rollouts-demo-active 8080:80 >/dev/null 2>&1 &
kubectl port-forward svc/rollouts-demo-preview 8081:80 >/dev/null 2>&1 &
sleep 3

Hit each one using:

echo "active : $(curl -s http://127.0.0.1:8080/color)"
echo "preview: $(curl -s http://127.0.0.1:8081/color)"

Output:

active : "blue"  
preview: "yellow"

This is exactly the window where you’d run smoke tests, point a staging frontend at the preview hostname, or have Argo run an AnalysisTemplate against Prometheus.

Nothing about production traffic has changed yet.

When you’re done with the port-forwards, run the following command:

kill %1 %2 2>/dev/null

Step 7: Promoting

When you’re happy, flip the active Service over with one command:

kubectl argo rollouts promote rollouts-demo

Output:

rollout 'rollouts-demo' promoted

Argo updates the active Service’s selector to the new ReplicaSet hash.

Subsequent requests should show production traffic is in yellow. The old blue pods stick around for scaleDownDelaySeconds (30 by default) before being torn down, which is what makes the next section possible.

Confirm the cutover by running:

kubectl argo rollouts status rollouts-demo --timeout 60s

You should see Healthy, and the Service selectors should now agree:

kubectl get svc rollouts-demo-active rollouts-demo-preview \
  -o jsonpath='{range .items[*]}{.metadata.name}{" -> hash="}{.spec.selector.rollouts-pod-template-hash}{"\n"}{end}'

Output:

rollouts-demo-active -> hash=7cf9dff6bb
rollouts-demo-preview -> hash=7cf9dff6bb

Step 8: Rolling back

If something goes wrong after the promotion (a metric tanks, you spot an error in the logs, a teammate flags a bug), undo it by running:

kubectl argo rollouts undo rollouts-demo

That brings the previous ReplicaSet back as the new “preview” and pauses, waiting for you to confirm with promote once more, which flips the active Service back to it. Because the old pods were kept warm by scaleDownDelaySeconds, this happens in seconds, not whatever your image pull time is.

Step 9: Cleaning up

Once you’re done, you can tear down the demo using:

kubectl delete -f rollout.yaml
helm uninstall argo-rollouts -n argo-rollouts
kubectl delete ns argo-rollouts

How does this fit with Octopus Deploy and Argo CD

Everything we’ve done so far works on its own. You’ve got a Rollout, two Services, and a one-command promote/undo loop.

Argo Rollouts is happy to do its job at the cluster level. What it doesn’t have is an opinion on how dev becomes staging and then becomes production. Who’s allowed to push the button, or what the deployment history looked like six weeks ago.

This is the layer Octopus Deploy is built for. A good mental model is:

  • Argo Rollouts owns the cluster-side mechanics: Which ReplicaSet is active, which is preview, when to flip, and when to scale down old pods.
  • Argo CD owns the GitOps sync: The Rollout (and its Services) live in a Git repo, and the cluster state is reconciled to match.
  • Octopus owns everything above that: Environments, approval gates, release lifecycles, audit trails, and the self-service UI that developers actually click on.

The promotion path between environments is described once in Octopus and reused across every service, instead of being re-encoded in each team’s CI script.

Ship green, sleep through the night

If you made it this far, you’ve got the cluster-side mechanics of progressive delivery sorted: a Rollout flipping between active and preview Services, a manual promotion gate, and instant rollback. That’s the hard, hands-on layer done.

What’s missing is the orchestration above it, including environments, approvals, audit trails, and the self-service flow your developers actually click. That’s where Octopus Deploy slots in, sitting on top of Argo CD and Argo Rollouts to give you a complete progressive delivery stack across every environment, not just one cluster.

Connect your Argo CD instance to Octopus and see how the whole pipeline comes together, or try Octopus free and wire it up against your own cluster.

Happy deployments!

Read the whole story
alvinashcraft
8 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Announcing Rust 1.96.1

1 Share

The Rust team has published a new point release of Rust, 1.96.1. Rust is a programming language that is empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, getting Rust 1.96.1 is as easy as:

rustup update stable

If you don't have it already, you can get rustup from the appropriate page on our website.

What's in 1.96.1

Rust 1.96.1 fixes:

It also fixes three CVEs affecting libssh2 (which is compiled into Cargo):

Contributors to 1.96.1

Many people came together to create Rust 1.96.1. We couldn't have done it without all of you. Thanks!

Read the whole story
alvinashcraft
8 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Nullable GUID Route Constraints in ASP.NET Core

1 Share

This is the ASP.NET Core companion to my original post on NullableGuidConstraint for ASP.NET MVC & WebApi.

The original article implements one constraint for both traditional ASP.NET MVC and Web API. ASP.NET Core unified those programming models and includes a built-in guid route constraint, so the custom compatibility constraint is no longer needed in modern applications.

Optional nullable GUID parameters

Combine the built-in guid constraint with the optional-route marker:

[HttpGet("customers/{id:guid?}")]
public IActionResult Get(Guid? id)
{
    return id is null
        ? Ok("Return all customers")
        : Ok($"Return customer {id}");
}

This route matches both:

/customers
/customers/0f8fad5b-d9cb-469f-a165-70867728950e

It does not match a non-GUID value such as:

/customers/not-a-guid

Required GUID parameters

Remove ? when the route requires a GUID:

[HttpGet("customers/{id:guid}")]
public IActionResult Get(Guid id)
{
    return Ok($"Return customer {id}");
}

Minimal APIs

The same constraint syntax works with minimal API route handlers:

app.MapGet("/customers/{id:guid?}", (Guid? id) =>
{
    return id is null
        ? Results.Ok("Return all customers")
        : Results.Ok($"Return customer {id}");
});

Constraint versus validation

Route constraints help endpoint selection. They are not a replacement for validating business rules.

Use {id:guid} when a non-GUID path should not select the endpoint. Validate rules such as whether the GUID exists, is allowed, or must not be Guid.Empty inside the endpoint or application layer.

For traditional ASP.NET MVC and Web API, including the shared custom NullableGuidConstraint, see the original article.

Read the whole story
alvinashcraft
8 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

What's new in Astro - June 2026

1 Share
June 2026 - Astro 7, summer swag collection, Astro Germany meetup, and more!
Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

Beyond Prompt Injection

1 Share

In late 2025, the security community stopped treating indirect prompt injection as a theoretical risk. It had spent two years as a tidy lab demonstration; then production systems started getting hit. The OWASP Top 10 for LLM applications now ranks prompt injection as the number-one risk, NIST has called indirect injection generative AI’s greatest security flaw, and academic researchers showed that a single poisoned email could coerce a model into exfiltrating SSH keys in up to 80% of trials, with zero user interaction. The attack needs no malicious binary, no phishing clicks, and no anomalous login. The agent simply reads content and takes action, exactly as designed, and the content was written by an attacker.

The most instructive example is ForcedLeak. In September 2025, researchers at Noma disclosed a critical vulnerability chain (CVSS 9.4) in Salesforce’s Agentforce platform: An attacker embedded malicious instructions in the description field of a routine Web-to-Lead form. The text sat harmlessly in the CRM until an employee later asked the AI agent to process that lead, at which point the agent dutifully executed both the legitimate query and the attacker’s hidden payload, exfiltrating sensitive CRM data to an external server. The detail that should keep you up at night is that the exfiltration destination was a domain still on Salesforce’s trusted allowlist, one that had expired and which the researchers re-registered for about five dollars. Every security control saw legitimate traffic to a trusted domain. Nothing looked wrong.

If your instinct reading that is “we filter for prompt injection,” you’re defending the wrong perimeter. Input filtering is necessary but nowhere near sufficient. The uncomfortable truth is that the injection isn’t the breach; the action is. And almost everything we call “AI security” is aimed at the wrong half of that sentence.

The defense everyone is building

Ask most enterprise AI teams how they secure their agents, and you’ll hear a consistent answer: They sanitize inputs. They harden system prompts with elaborate instructions to ignore conflicting directives. They run classifiers over incoming content to flag adversarial patterns. Some have adopted the more sophisticated training-time defenses the frontier labs have published—instruction hierarchies that teach a model to assign differential trust to different sources and reinforcement-learning approaches that harden models against injection in agentic contexts.

All of this is good work, and none of it should be abandoned. But notice what every one of these techniques shares. They all try to stop the model from being fooled. They assume that if we make the model robust enough at the input layer, the system is safe. That assumption is the vulnerability.

We’ve spent two years trying to make the model unfoolable. The systems that survive contact with production assume it will be fooled anyway.

Why the input layer is the wrong perimeter

Prompt injection isn’t a bug a future model will lack. It’s a structural property of how language models work. The model consumes a single undifferentiated stream of tokens at the moment of inference. Your instructions, the retrieved document, the tool output, and the web page just fetched are indistinguishable channels collapsed into one context. There’s no hardware-enforced boundary between “trusted instruction” and “untrusted data” the way there is between kernel space and user space in an operating system.

This is why the attack surface explodes the moment an agent becomes agentic. A chatbot that only talks is a contained risk. An agent that retrieves from the open web, reads email, queries databases, and calls APIs ingests adversarial content from a dozen sources on every turn, and any one of them can carry an instruction. Researchers cataloging real agent ecosystems have already found hundreds of malicious third-party extensions performing data exfiltration and silent injection without any user awareness. These aren’t laboratory curiosities. They’re the production environment.

So, if you can’t guarantee the model will never be fooled—and you can’t—then architecture that depends on it never being fooled is built on sand. You need a second principle, one distributed systems engineers have understood for decades.

Verify, then trust

The principle is simple to state and hard to retrofit: An agent’s proposed action should be validated against an external, deterministic policy before it executes, regardless of why the agent proposed it. The validator doesn’t ask whether the instruction that produced the action was legitimate. It doesn’t try to detect the injection. It asks a different and far more answerable question: Is this action, on its face, permitted?

This inverts the burden. Detecting a cleverly disguised malicious instruction is open-ended because the adversary gets to be arbitrarily creative. Checking whether a wire transfer exceeds a hard dollar limit is a closed problem with a definite answer. We move the security decision from where the attacker has infinite freedom to where they have almost none.

Crucially, the check must be deterministic code, not another model asking, “Does this look dangerous?” The moment you ask a second LLM to adjudicate, you’ve reintroduced the exact same vulnerability one layer down. The enforcement layer is boring, auditable conventional software, and that’s the point.

Here’s what it looks like in practice. An agent managing procurement proposes an action, and a runtime contract evaluates it before anything reaches a real API:

# agent_contract.yaml
 agent_id: "procurement_executor_07"
 role: "EXECUTOR"
 policy:
   approve_invoice:
 	max_amount_usd: 50000
 	allowed_vendors: from_approved_registry
 	require_human_above_usd: 10000

 # Runtime, on a proposed action:
 ACTION   approve_invoice(vendor='Acme', amount=1200000)
 REJECTED policy violation: max_amount_usd
      	proposed 1,200,000 / limit 50,000
      	action discarded, human notified, no API call made

The injected instruction at 2:14am never matters here. The agent can be perfectly, catastrophically fooled, and the wire transfer still doesn’t happen, all because a simple deterministic check stood between the model’s output and the outside world, and the proposed action failed it.

This only works if the action arrives structured, which makes structure a precondition.

The contract inspects approve_invoice (vendor, amount) cleanly only because the action is already typed. If the agent emits prose, “please approve the Acme invoice,” something has to parse it, and the only thing that parses open language is another LLM, so the indeterminacy walks back in. That dictates the design.

A consequential action must cross the boundary as a typed tool call, never as free text. Where the input is unavoidably natural—an email saying, “Wire them their balance” for example—let the model extract a structured value but never let its extraction be self-authorizing. The model proposes the amount; the gate still checks it against the limit, the vendor registry, and the actual balance in the system of record, not the number the email asserted. Extraction is probabilistic, while validation stays deterministic.

A few decisions are pure judgment with no schema, such as “Is this email phishing?” There the model stays in the loop. You bound the consequences instead, with reversibility and human review above a threshold. Contracts protect parameterizable actions, and unparameterizable judgments fall back to containment.

The architecture this implies

Once you accept that the action layer is where security lives, three design commitments follow, and they map almost directly onto principles that hardened distributed systems years ago.

Least privilege for agents, scoped to the action, not the agent. The naive version assumes you can predict what an agent will do and provision it accordingly. For a specialized agent you can: One that only summarizes has no business holding a credential that moves money. But the agents people actually reach for are general. In a single session, I might ask a coding agent to summarize a file, write code, execute it, and query company data—four tasks with four risk profiles, none of which are enumerated in advance. Static least privilege collapses the moment one identity spans that range.

The fix is to make privilege a property of the action, not the agent. The agent holds no dangerous capability by standing grant; it requests narrow, transient elevation per action, which the same deterministic gate approves or denies. Reading a document is auto-approved; querying the warehouse is not. The dangerous credential exists only for the instant the action is permitted, then evaporates. One caveat: This governs what an agent may reach but not what the code it writes then does. Executing code can be gated as a capability, but what executes still needs containment, sandboxing, and egress control, because generativity is a different problem from access.

Zero trust for machine identities. Every action an agent takes should be authenticated and authorized as if it came from an untrusted actor, because, functionally, it might be acting on an attacker’s instructions. The proliferation of agents has expanded the attack surface faster than most identity systems were designed to handle, and treating agent traffic as inherently trusted because it originates inside your own system is precisely the mistake.

Capability contracts at the boundary. Every consequential action passes through a deterministic gate that encodes what is allowed, dollar limits, rate limits, allowlisted destinations, mandatory human review thresholds. The contract is version-controlled, auditable, and lives entirely outside the model.

The trap of normalized deviance

The quieter organizational danger is the slow accumulation of false confidence from connecting insecure agents to real systems and watching nothing bad happen. . .for a while. Researchers have warned about indirect injections for years, but most deployments have gotten away with it. Each uneventful day makes the next risky connection feel safer. This is the normalization of deviance. Every system that eventually failed catastrophically felt the same way: fine, fine, fine, until it wasn’t.

The teams that will weather the coming wave of agent incidents aren’t the ones with the cleverest input filters. They’re the ones who assumed compromise from the start and built the boring enforcement layer anyway, the ones who decided that an agent’s autonomy ends precisely at the point where it tries to do something irreversible.

Where to start on Monday

You don’t need to rearchitect everything. Start by inventorying the actions your agents can take, and sort them by blast radius: What’s the worst thing that happens if this action fires when it shouldn’t? For every high-blast-radius action, write a deterministic contract that gates it and put a human in the loop above a threshold you can defend to your risk team. Then, and only then, keep hardening your inputs.

Prompt injection won’t be solved at the input layer, because it can’t be. But it can be rendered survivable at the action layer, where deterministic code gets the final word. The model’s job is to be useful. Your architecture’s job is to make sure that when the model fails—or worse, when it has been turned against you—the failure stops at the gate.



Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

The Anti-Pattern Bingo Team—When Success Is a Zero-Sum Game | Gunnar Fischer

1 Share

Gunnar Fischer: The Anti-Pattern Bingo Team—When Success Is a Zero-Sum Game

Read the full Show Notes and search through the world's largest audio library on Agile and Scrum directly on the Scrum Master Toolbox Podcast website: http://bit.ly/SMTP_ShowNotes.

 

"This was neither Scrum, nor a team. It was more like the anti-pattern bingo team." - Gunnar Fischer

 

When Gunnar took his first job abroad, he walked straight into what he calls the "anti-pattern bingo team"—a supposed Scrum team that was neither Scrum nor a team. The company was in trouble, the team had a bad reputation and was used as a punching bag, the manager-of-manager treated them like a stepchild, and the new manager didn't seem to know what he had signed up for. The team members themselves didn't really want to work together. The goals slipped. Decisions were made in back rooms, outside the official meetings. And underneath it all sat the most corrosive belief Gunnar names: that success is a zero-sum game—if you win, I lose. With that mindset, there is no team, just individuals defending turf. One pattern stuck with him so clearly he gave it a name: sandcastle planning. The team would finish a Sprint planning, agree on a goal, and the manager would walk in right after the meeting and overturn the whole thing with his own priorities. Over time, the team stopped putting effort into planning. Why build the castle if someone will trample it? Even worse, when an escalation finally surfaced, Gunnar—an immigrant—was told that as a German, he must be "very authoritarian." A label, served up as analysis. That was the moment he knew the team would never have safe disagreements, never reach the right level of challenge, never recover.

 

In this segment, we talk about scrum master anti-patterns, the corrosive effect of treating people as labels, and how the absence of an explicit reason for the team's existence makes everything else collapse.

 

Self-reflection Question: Does your team have a clear, explicit reason for existing—or are you just a group that shares a technology, a building, or a reporting line?

Featured Book of the Week: Scrum Mastery (2nd Edition) by Geoff Watts

For Gunnar, the book that shaped him most as a Scrum Master is Scrum Mastery (2nd Edition) by Geoff Watts—what he calls "the noble knight of Scrum books." He first read it just after a Scrum course and thought, "What should I even do with this kind of wisdom?" Years later he came back to it and understood. A few years after that, he became modest about it: these are truths, but it's about making them true—and watching for when they aren't. The book is full of phrases like "a good Scrum Master is indispensable; a great Scrum Master is dispensable and wanted." That last line captured it perfectly for him: success isn't being unneeded, it's being chosen. As Gunnar puts it: "In times when everybody is challenged and people are making fun of agile practitioners, this brings back all of the ideals of what a Scrum Master is really about." You can also listen to our previous episodes with Geoff Watts on the podcast.

 

[The Scrum Master Toolbox Podcast Recommends]

🔥In the ruthless world of fintech, success isn't just about innovation—it's about coaching!🔥

Angela thought she was just there to coach a team. But now, she's caught in the middle of a corporate espionage drama that could make or break the future of digital banking. Can she help the team regain their mojo and outwit their rivals, or will the competition crush their ambitions? As alliances shift and the pressure builds, one thing becomes clear: this isn't just about the product—it's about the people.

 

🚨 Will Angela's coaching be enough? Find out in Shift: From Product to People—the gripping story of high-stakes innovation and corporate intrigue.

 

Buy Now on Amazon

 

[The Scrum Master Toolbox Podcast Recommends]

 

About Gunnar Fischer

 

Gunnar is the leader of the Chocolate Guild. Agile practitioner with a software developer background and a strong interest in people, intercultural contacts and the bigger picture. Gunnar's purpose is to teach and to learn, to grow as a person and to support others who want the same.

 

You can link with Gunnar Fischer on LinkedIn.

 

You can also read Gunnar's writing on his blog, Leader of the Chocolate Guild.





Download audio: https://traffic.libsyn.com/secure/scrummastertoolbox/20260630_Gunnar_Fischer_Tue.mp3?dest-id=246429
Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories