Blog

Team Topologies

As I mentioned in my previous post, when I attended the Fast Flow Conference 25 in Den Bosch (https://www.fastflowconf.com/), I got the know the concept Team Topologies. Afterwards, I bought the book Team Topologies written by Matthew Skelton (https://blog.matthewskelton.net/) and Manuel Pais (https://www.manuelpais.net/)/

The book introduces a point of view which is not so different what was explained in The Accelerate, DevOps Handbook etc. However, it is definitely a book worth reading because authors explain the modern software development challenges and potential solutions to them with quite amount of business cases.

The book contains three parts: Teams as the means of delivery, Team topologies that work for flow and Evolving team interactions for innovations and rapid delivery. Each part has several chapters focusing on different areas of sofware development teams.

It bases Conway’s law and evolves around it. Therefore it is good to understand what Conway’s law before reading the book.

Conway’s law

Let’s first understand what Conway’s law is;

“Any organization that designs a system will produce a design whose structure is a copy of the organization’s communication structure.”
— Melvin E. Conway, 1968

In simpler terms, the way your teams are structured and communicate will shape the architecture of the software they build.

When people collaborate, their communication paths influence how problems are broken down and solved. If teams are siloed or specialized, systems tend to reflect those divisions. For example:

  • If you have four separate teams, you’ll likely end up with a system split into four main modules, even if a different structure might be more optimal.
  • If your frontend and backend teams rarely talk, the integration between them may be clunky or inefficient.

The book Team Topologies takes this law as reference point and explains how modern software development teams are built against it and of course it gives some ideas and tips how to solve certain challenges.

The fast flow

One of the concepts mentioned is called fast flow.

Fast Flow refers to the ability of an organization to quickly and safely deliver changes to production, from idea to value, with minimal friction. It’s not just about speed — it’s about reliable, sustainable, and continuous delivery of improvements.

Certain obstacles to fast flow are; Pushing against Conway’s law, Software too big for teams, Confusing organizational design options, Team pulled in many directions, Painful re-organizations every few years, Flow is blocked, Too many surprises and Disengaged teams.

This ties directly back to Conway’s Law: to achieve Fast Flow, team structures must support the system architecture you want. That means:

  • Teams should be organized around value streams or products, not layers or technologies.
  • Use stream-aligned teams that own the full lifecycle of a part of the system.
  • Keep dependencies between teams low, so teams can move independently and safely.
Team-First Design

Team-First Design is the idea that you should design your software architecture and systems around the needs and capabilities of your teams, not the other way around.

Instead of forcing teams to fit into a rigid technical design, you shape your system to fit how your teams work best — optimizing for clarity, autonomy, and fast flow. This is one of the main problems in modern organizations. When re-organization is planned, the team dynamics are not taken into account. Instead, teams are expected to adapt the new way of working. In theory this all works however in practice most of those plans are failing. Team topologies adresses this problem in efficient way so that team dynamics, capabilities must be taken into account during the design.

The 4 Fundamental Team Topologies

In Team Topologies, the authors define four fundamental types of teams, each with a clear purpose and mode of interaction. These team types help organizations design team structures that support Fast Flow, reduce friction, and improve autonomy.

Stream-Aligned Team

A stream-aligned team is aligned to a flow of work from a business domain, such as a product, service, customer segment, or user journey. It’s purpose to deliver end-to-end value independently and continuously.

Key characteristics:

  • Owns the entire lifecycle (build, run, support)
  • Cross-functional (e.g., includes dev, QA, UX, etc.)
  • Can release to production without external dependencies

Example:
A team focused on the checkout process in an e-commerce app.

Enabling Team

An enabling team helps other teams adopt new tools, practices, or skills by coaching, sharing expertise, and removing blockers. This teams main purpose is to accelerate learning and adoption of modern practices across teams.

Key characteristics:

  • Short-lived or long-lived based on need
  • Works by collaborating, not delivering features directly
  • Focused on uplifting capabilities (e.g., observability, test automation, security practices)

Example:
A team helping stream-aligned teams adopt Kubernetes or CI/CD pipelines.

Complicated Subsystem Team

A complicated subsystem team focuses on a part of the system that requires deep specialist knowledge, such as algorithms, math-heavy components, or real-time systems. This team develop and maintain high-complexity components so other teams don’t need to understand the deep internals.

Key characteristics:

  • Small, focused, high expertise
  • Interfaces clearly defined for others to consume
  • Often works with stream-aligned teams who use their subsystem

Example:
A team maintaining a fraud detection engine using machine learning.

Platform Team

A platform team builds and maintains internal platforms that make it easier for stream-aligned teams to deliver value. Platform team provide self-service tools and APIs that reduce cognitive load on product teams.

Key characteristics:

  • Treats platform as a product (serving internal users)
  • Encourages self-service and autonomy
  • Avoids becoming a blocker; aims to enable, not control

Example:
A team providing deployment pipelines, logging infrastructure, or a developer portal.

Conclusion

I definitely recommend this book to leaders in software development business. It gives a different point of view and helps today’s leaders to understand how other companies overcome similar software development challenges.

Fast Flow Conference 25

On 27th March, I attended the first Fast Flow Conference in the Netherlands. It was a whole day event around the topic: Team Topologies. There were quite amount of presentators who shared their journeys about in their business. The program was so full, they had to divide the presentations into two different hall so that I had to choose between halls. At some point I was going from one hall to another. At the end they shared all the recordings with me so I could watch the ones that I could not attend.

It was an event starting from 09:30 to 17:30 and heavily informative. At the end, I was overwhelmed with all those experiences and happy to collect all book advise 🙂

Team Topologies

According to their website:


Team Topologies is an approach to designing team-of-teams organizations for fast flow of value.

The approach is based on years of research across many organizations from different industries, different sizes and different technological backgrounds. It addresses the most dreaded challenges in technology:

  1. How to maintain or even accelerate speed of value creation while scaling the organization?
  2. How to evolve an architecture along with the evolution of the organization and avoid that one becomes a blocker for the other and a blocker for the growth of the business?

The approach provides a pattern language and a thinking model, which allows you to identify the team-of-teams design most appropriate for the target architecture and fast flow of value.


As you can see, they are trying to optimize one of the modern organization challanges: flow. But what is flow anyway?

Flow

In the context of the book, flow means delivering the work smoothly and continuously. It is all about bringing the value to the user/customer as short, fast, robust as possible. This is not a new challenge itself. For years scientists or businessmen are trying to find an ultimate solution for the same problem. We have heard many of them such as Lean, Kanban, Agile, DevOps, SaFE etc. In fact, they all are effective frameworks however there is no one framework solves it all! Those are solutions to different problems. Therefore we also need to understand what Team Topologies tries to solve and how?

Ideally each organization/team or process should be optimized specifically based on its needs. We need to understand all those frameworks and then apply them when needed.

I already ordered a copy of the book, and looking forward to read it.

I will share more when I read the book further and learn from it.

Delete stale branches automatically

Recently I wanted to do some cleanup and re-organization in Azure DevOps (ADO) project. Removed some teams, reviewed branch policies etc. During this cleanup I saw those long living development branches in the repositories. Normally, when a Pull Request (PR) is completed, ADO gives an option to delete source branch when the PR is completed.

Do not forget to delete source branch

However, sometimes developers do not click this option. Sometimes they create a branch, push it to remote but then forget about it. There are many reasons branches are created and then remain there for a long time. If it is few, then I go an delete them one by one. However in the last project, there were over 20 repositories and removing them manually did not make any sense. On top of that, they would grow again instantly. Therefore I decided to do some automation for the operation.

Pipelines are the best

I decided to create a pipeline which would go through each repository, then do some operation for me. So I made my plan as;

  • Go through all the repositories in selected project
  • Find all short living branches (I exclude main/master/release branches because those are long living branches)
  • Check if there are any open pull request for this branch. If so, then skip
  • Check if there are any commit in the last 2 weeks for this branch. If so, then skip.
  • Delete the branch.
  • Continue

Idea was simple. Couple of iterations, some simple API calls and lots of validations. I wanted to use ADO Rest APIs and PowerShell script to write my code.

# Azure DevOps Organization and Project Details
$organization = "https://dev.azure.com/myOrganization"
$project = "myProject"
$accessToken = "myPAT"

Write-Host "Fetching repositories from project '$project'..."

# Authorization Header
$headers = @{
   Authorization = "Bearer $accessToken"
}

After preparing the pre-requisities, I started fetching the data.

# Get Repositories
$reposUrl = "$organization/$project/_apis/git/repositories?api-version=7.2-preview.1"
$repositories = (Invoke-RestMethod -Uri $reposUrl -Headers $headers).value

    Write-Host "Found $($repositories.Count) repositories."

    foreach ($repo in $repositories) {
    $repoId = $repo.id

    if (-not $repoId) {
       Write-Warning "Repository '$($repo.name)' does not have a valid ID. Skipping."
       continue
    }

    Write-Host "Processing repository: $($repo.name) ($repoId)..."

    $branchesUrl = "$organization/$project/_apis/git/repositories/$repoId/refs?filter=heads/&api-version=7.2-preview.1"
    $branches = (Invoke-RestMethod -Uri $branchesUrl -Headers $headers).value

    foreach ($branch in $branches) {
        $branchName = $branch.name -replace "refs/heads/", ""

        Write-Host "Checking branch: $branchName..."

        if ($branchName -notin @("main", "release", "master")) {
            # Check for open pull requests
            $prUrl = "$organization/$project/_apis/git/repositories/$repoId/pullRequests?searchCriteria.status=active&searchCriteria.sourceRefName=refs/heads/$branchName&api-version=7.2-preview.2"
            $prs = (Invoke-RestMethod -Uri $prUrl -Headers $headers).value

            if ($prs.Count -gt 0) {
                Write-Host "Branch '$branchName' has open pull requests and was skipped."
                continue
            }

            Write-Host "Branch '$branchName' does not have any open pull requests"

            if ($prs.Count -eq 0) {
                # Check last commit date
                $commitsUrl = "$organization/$project/_apis/git/repositories/$repoId/commits?searchCriteria.itemVersion.version=$branchName&searchCriteria.$top=1&api-version=7.2-preview.2"
                $lastCommit = (Invoke-RestMethod -Uri $commitsUrl -Headers $headers).value[0]

                if (-not $lastCommit) {
                    Write-Warning "Branch '$branchName' has no commits. Skipping."
                    continue
                }

                if ($lastCommit) {
                    $commitDate = [datetime]$lastCommit.author.date

                    Write-Host "Last commit on branch '$branchName' was on $commitDate."

                    if ((Get-Date) - $commitDate -le 14) {
                        Write-Host "Branch '$branchName' has recent commits. Skipping."
                        ontinue
                    }

                    if ((Get-Date) - $commitDate -gt 14) {
                        # Try to delete branch
                        $lastCommitId = $lastCommit.commitId
                        Write-Host "Attempting to delete branch: $branchName..."
                        Write-Host "Last commit id is $lastCommitId"

                        try {
                            $deleteUrl = "$organization/$project/_apis/git/repositories/$repoId/refs?api-version=6.1-preview.1"
                            $payload = @(
                                @{
                                    name = "refs/heads/$branchName"
                                    oldObjectId = $lastCommitId
                                    newObjectId = "0000000000000000000000000000000000000000"
                                }
                            ) | ConvertTo-Json -Depth 10 -Compress

                            $payload = "[$payload]"

                            $response = Invoke-RestMethod -Uri $deleteUrl -Method Post -Headers $headers -Body $payload -ContentType "application/json"
                            Write-Host "Response: $response | ConvertTo-Json -Depth 10"
                                          
                            if ($response.value[0].success -eq $true) {
                                Write-Host "Deleted branch: $branchName"
                            } else {
                                Write-Warning "Failed to delete branch '$branchName': $($response | ConvertTo-Json -Depth 10)"
                            }
                            catch {
                                $responseContent = $null
                                if ($_.Exception.Response -ne $null -and $_.Exception.Response.Content -ne $null) {
                                    try {
                                        # Access content before it's disposed
                                        $responseContent = $_.Exception.Response.Content.ReadAsStringAsync().Result
                                    } catch {
                                          Write-Warning "Failed to read error response content: $($_.Exception.Message)"
                                      }
                                    }
                                          
                                    Write-Warning "Failed to delete branch '$branchName': $($_.Exception.Message)"
                                    if ($responseContent) {
                                        Write-Warning "Detailed error response: $responseContent"
                                    }
                                 }
                            }
                        }
                      } else {
                              Write-Host "Branch '$branchName' has open pull requests and was skipped."
                      }
                  }
              }
          }

I must say figuring out the working code was quite a challenge. Many trial-errors but finally I made it working. There are of course some things I had figured out after some research and lots of documentation reading.

POST https://dev.azure.com/fabrikam/_apis/git/repositories/{repositoryId}/refs?api-version=7.1

{
  "name": "refs/heads/vsts-api-sample/answer-woman-flame",
  "oldObjectId": "0000000000000000000000000000000000000000",
  "newObjectId": "ffe9cba521f00d7f60e322845072238635edb451"
}

However this failed with an error message. After some review I noticed the documentation has a sample post method and there I figured out the array brackets. After adding the brackets, the issue is resolved.

Increasing the security

My code was working, but I was not happy with storing the PAT in the code as it is. Therefore I wanted to store it in a Key Vault, and read it from there during the run. For that, I added my PAT as a secret to Key Vault and updated my pipeline code accordingly.

steps:
    - task: AzureKeyVault@2
      displayName: "Fetch Token"
      inputs:
          azureSubscription: "mySubscription"
          KeyVaultName: "myKeyVaultName"
          SecretsFilter: "delete-branch-pipeline-token"
          RunAsPreJob: false

    - task: PowerShell@2
      displayName: "Delete Stale Branches"
      inputs:
          targetType: "inline"
          script: |
              # Azure DevOps Organization and Project Details
              $organization = "https://dev.azure.com/myOrganization"
              $project = "myProject"
              
              Write-Host "Fetching repositories from project '$project'..."

              # Authorization Header
              $headers = @{
                  Authorization = "Bearer $(delete-branch-pipeline-token)"
              }

That way my PAT was not stored in source code and could not be seen during the pipeline run either.

Mermaid: Creating diagrams using Markdown

As a part of my job, I am spending quite amount of time for documentation. Flowcharts, diagrams, class designs and many more. One of the challenges or creating diagrams are they are difficult to revise. My normal flow is to create a diagram in a tool (i.e draw.io) and then export it as .svg or .pdf format. Afterwards, use this exported file in the documentation.

Downside of this approach is, I cannot see the older versions or what kind of changes I am making during the new version of documentation.

It is a JavaScript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically.

I was looking for a tool to solve this issue when I found out Mermaid. In their site, they define themselves as a JavaScript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically.

I gave it some try and must say they do a good job. However for complex flowcharts things are easily getting out of hand with links going all over the place.

I will give it more try to find out whether I can make the best out of it.

A small step into wilderness

Not all those who wander are lost

It has been a very long time that I had this idea. To write a personal blog. Finally, the dream come true.

I will write here about the topics that I like. Let this be the first post and see what the future brings.