The site that you are currently viewing is a static version of the Software Factory documentation delivered with the

version v7.2.
For up-to-date documentation, see the latest version.

Environments for Different Data Sensitivity

Decision framework for organizing development environments when handling different levels of data sensitivity.

Introduction

When developing software systems that handle different levels of data sensitivity, the default approach is to use a single environment for all development activities.

A single environment provides:

  • Operational simplicity: One set of tools, processes, and credentials to manage
  • Unified collaboration: All teams work together without coordination barriers
  • Reduced cognitive load: Developers focus on building features, not navigating multiple environments
  • Lower costs: No duplication of infrastructure, tooling, or processes

However, the more sensitive the data an environment handles, the more constraints it imposes—in terms of access restrictions, available tooling, network limitations, and operational overhead. These constraints can significantly impact development velocity and team productivity.

In such contexts, a project may consider using multiple environments to keep non-sensitive development activities in a less constrained space. But this approach comes with its own challenges and requires careful planning. This guide helps you evaluate whether splitting environments makes sense for your project and how to do it successfully.

Note

In this context, an “environment” refers to both the Software Factory (development tools, CI/CD pipeline, version control) and the deployment platforms. A single environment means teams use the same factory and deploy to the same platform.

Opening Remarks

Before using this decision framework, you must conduct a data sensitivity assessment of your project to classify the different levels of data sensitivity your application will handle.

This assessment is critical because it directly influences your architectural decisions.

Your software architecture must reflect and enforce data sensitivity boundaries to enable proper isolation. Each sensitivity level should align with clear architectural boundaries—whether through separate bounded contexts, services, modules, or layers—allowing you to physically isolate components based on their data sensitivity.

Without this architectural alignment, splitting environments becomes impractical or impossible: you cannot separate what is architecturally coupled. The data sensitivity assessment reveals where isolation is required, while your architecture determines whether isolation is feasible.

When to Consider Splitting Environments

Despite the benefits of a single environment, certain constraints may lead organizations to consider splitting their development environments:

  • Access constraints: The most restrictive environment enforces strict access policies that limit who can work within it based on individual profiles—such as security clearance level, organizational affiliation, or nationality (ECC-R, ECC-I, …). Team members who do not meet these criteria are physically unable to access this environment. This creates a forced split scenario where access restrictions prevent all teams from working together.
  • Productivity optimization: Higher sensitivity environments are typically less powerful, less up to date, and more expensive than lower sensitivity ones. When a significant portion of the codebase handles non-sensitive data, developing it in a less restrictive environment enables modern tooling, remote access, and flexible workflows—at lower cost and without the security overhead of highly controlled environments.
  • Attack surface reduction: Hosting non-sensitive code in the most restrictive environment means granting access to that environment to a larger number of people—including those who have no need to know about its sensitive data. This violates the need-to-know principle and increases the attack surface. Keeping non-sensitive development in a lower sensitivity environment limits the number of people with access to the most restricted assets.
  • Cross-project sharing: Reusable components—libraries, building blocks, tools—that are intended to be shared across multiple projects benefit from being developed in the less restrictive environment. Hosting them in a lower sensitivity context maximizes their accessibility to a wider audience. This is particularly relevant for InnerSource practices, where broad access encourages contributions and collaborative development across the organization.
Tip

Evaluate the code ratio: Splitting environments only makes sense when a significant portion of the codebase is non-sensitive. If 90% of your code handles sensitive data, the overhead of maintaining a separate environment for the remaining 10% likely outweighs the benefits. Conversely, if only 5-10% of your code is sensitive, keeping most development in a less constrained environment can significantly improve productivity.

Impact and Cost of Split Environments

Splitting environments is not a simple operational decision—it has deep organizational, architectural, and operational implications that require careful alignment across team structure, software design, testing practices, and delivery pipelines. Misalignment creates coordination overhead, reduces team autonomy, and can negate the intended benefits.

Warning

Split environments introduce significant additional costs:

  • Infrastructure and tooling duplication
  • Synchronization and delivery mechanisms between environments
  • Increased coordination overhead between teams
  • Architectural refactoring to support loose coupling
  • More complex testing and integration strategies

These costs must be thoroughly evaluated and estimated before deciding to split environments.

About This Guide

This guide provides reflection and evaluation frameworks to help your project:

  • Understand the implications across team organization, software architecture, testing strategy, and build & delivery
  • Identify strategies to minimize impacts and adapt your approach based on your organizational and technical realities

The goal is to make informed decisions with full awareness of the trade-offs, costs, and organizational changes involved.

Quick Reference: Key Questions

Before diving into the details, here are the essential questions to evaluate for each dimension:

DimensionKey QuestionsDetails
PrerequisitesHave you conducted a data sensitivity assessment? Does your architecture reflect sensitivity boundaries? What is the ratio of sensitive vs. non-sensitive code?Prerequisites
Team OrganizationCan each team work in a single environment? Do you need a platform team to maintain coherence?Team Organization
Software ArchitectureIs your architecture loosely coupled at the sensitivity boundary? Can components be deployed independently?Architecture
Testing StrategyCan each team test autonomously without access to the other environment? Is your software designed for testability?Testing
Build & DeliveryAre CI/CD pipelines coherent across environments? Which delivery approach fits your context (artifact vs. code)?Build & Delivery
Tip

If you answer “no” to most of these questions, splitting environments will likely create more problems than it solves. Consider staying with a single environment or addressing these gaps first.

Making Split Environments Successful

Split environments are only viable when four interconnected dimensions are properly aligned:

  • Team Organization
  • Software Architecture
  • Testing Strategy
  • Build & Delivery
graph LR
    Teams[Team<br/>Organization] --> SoftwareArch[Software<br/>Architecture]
    SoftwareArch --> Testing[Testing<br/>Strategy]
    Testing --> BuildDelivery[Build &<br/>Delivery]
    BuildDelivery --> Teams

These dimensions influence each other and must be addressed together—neglecting anyone creates coordination overhead, coupling issues, or reduced team autonomy.

Team Organization Principles

When splitting environments, team organization determines success or failure. Teams must maintain autonomy while working across different security contexts.

One Team, One Environment

For a team to remain autonomous and productive, it must work in a single, stable environment.

When a team operates across multiple environments, it faces significant overhead:

  • Cognitive load : Context-switching between different credentials, toolchains, network access, and workflows creates mental burden that reduces productivity and increases error risk
  • Access management complexity: Maintaining multiple sets of credentials and security compliance requirements
  • Workflow fragmentation: Different processes and tools in each environment disrupt team flow
  • Reduced autonomy: Dependencies on cross-environment coordination slow down delivery

Teams working in a single environment maintain focus, optimize their workflows for their specific context, and retain the autonomy needed for fast delivery.

Teams

Note

For projects with a single team, splitting environments makes no sense—the team would bear all the complexity without organizational benefit. Single team projects should always use a single environment.

Endpoint Availability

The “one team, one environment” principle has a physical prerequisite: each environment is only accessible from specific endpoints (workstations, virtual desktops, or secure terminals). Before committing to a split environment strategy, the following questions must be evaluated:

  • Integration into the IS/IT: Are the required endpoints for each environment integrated into the organization’s information system? Some high-sensitivity environments require dedicated, specially configured workstations that may not be part of the standard IT landscape.
  • Availability and provisioning: Are enough endpoints available for all team members who need access? Procurement and provisioning of secure workstations can involve long lead times.
  • Workstation multiplication: In a split environment scenario, developers who need to interact with multiple environments may require multiple physical workstations—one per environment. This multiplies hardware costs, workspace requirements, and IT support overhead.
Warning

Endpoint constraints can silently undermine a split environment strategy. If the required workstations are not available, not integrated, or insufficient in number, teams will face access bottlenecks that negate the expected productivity gains. Assess endpoint readiness early in the decision process.

The following is a non-exhaustive list of available endpoints. Refer to each endpoint’s documentation for details on supported environments and access conditions:

Platform Team for Environment Coherence

Team Topologies —a book by Matthew Skelton and Manuel Pais based on extensive research into high-performing technology organizations—identifies fundamental team types optimized for fast flow of value. One of these types is the platform team: a team that provides internal services and capabilities that reduce the cognitive load on other teams.

When multiple teams work in different environments, maintaining coherence across environments—tooling versions, processes, standards, delivery pipelines—becomes a significant challenge. Each team dealing with these concerns independently creates duplication of effort and inconsistency.

In split environment scenarios, a dedicated platform team is strongly recommended to manage this complexity and reduce cognitive load on development teams. Rather than each team understanding the intricacies of both environments and their synchronization, the platform team abstracts this complexity and provides a consistent experience.

This platform team:

  • Maintains coherence between environments (tooling, processes, standards)
  • Manages the delivery pipeline between environments
  • Provides self-service capabilities to development teams
  • Reduces cognitive load by abstracting environment complexity

The platform team doesn’t own application code but enables development teams to focus on delivering business value without worrying about environment-specific concerns.

Teams

Conway’s Law Implications

Conway’s Law states: “Organizations design systems that mirror their own communication structure.”

When teams are organized by environment, the software architecture must reflect this structure. This alignment between team structure and architecture is key to achieving loosely coupled teams :

  • The team in the less restrictive environment owns loosely-coupled, non-sensitive components
  • The team in the restrictive environment owns sensitive components and final product assembly
  • Communication between teams happens through well-defined interfaces, not ad-hoc coordination

If your organization structure doesn’t match your desired architecture, one of them must change.

Architectural Requirements

Software architecture directly influences the other three dimensions and determines whether split environments are viable.

When architecture is tightly coupled across the sensitivity boundary:

  • Team organization suffers: Teams must constantly synchronize changes across environments, or individual team members must work across both environments—dramatically increasing cognitive load
  • Testing becomes difficult: Components cannot be tested independently in their own environment without dependencies on the other environment
  • Delivery complexity increases: Components cannot be built and delivered independently, creating coordination bottlenecks

Conversely, a loosely coupled architecture enables teams to work autonomously in their environment, test their components independently, and deliver value without cross-environment coordination.

Architectural Coupling Assessment

Evaluating architectural coupling is a critical decision point: tight coupling makes split environments impractical, while loose coupling enables them. This assessment should happen early, before committing to a split environment strategy.

graph TB
    subgraph loose["✅ Loose Coupling"]
        direction LR
        L1[Component A<br/>🔓Non-sensitive]
        L2[Component B<br/>🔒Sensitive]
        L1 -->|"Clear API<br/>Contract"| L2
    end

    subgraph tight["❌ Tight Coupling"]
        direction LR
        T1[Component A<br/>🔓Non-sensitive]
        T2[Component B<br/>🔒Sensitive]
        T1 <-->|Shared files| T2
        T1 <-->|Shared DB| T2
        T1 <-->|Direct calls| T2
    end

Evaluate your architecture by asking:

  • Can components in different environments be deployed independently without blocking each other?
  • Do components communicate through well-defined, stable contracts (APIs, ABIs, message schemas)?
  • Can a component in one environment evolve its implementation without forcing changes in the other?
  • Are domain boundaries clear, or does business logic span across the sensitivity boundary?

Characteristics of loose coupling (favorable for split environments):

Based on DORA’s loosely coupled architecture capability :

  • Independent deployability: Components can be deployed without blocking each other
  • Contract-based interfaces: Clear, versioned APIs, ABIs, or message contracts
  • Autonomous data management: Each component manages its own data; sharing through defined interfaces only
  • Domain alignment: The sensitivity boundary aligns with natural domain boundaries (Bounded Contexts )

Indicators of tight coupling (problematic for split environments):

  • Multiple teams regularly modify the same code files
  • Changes in one environment frequently require coordinated updates in the other
  • Shared data models or databases across sensitivity levels
  • Business logic or domain concepts spanning across environments
  • Difficult or impossible to deploy or test components independently

Decision guidance:

  • Loose coupling present: Split environments can work if team organization, testing, and delivery are properly aligned
  • Tight coupling present: Strongly prefer a single environment; the coordination overhead and complexity will likely outweigh split benefits
  • Forced split scenario: If access constraints force a split despite tight coupling, see Code Forking with bidirectional synchronization as a last-resort solution with significant ongoing costs

Domain-Driven Design Alignment

Domain-Driven Design (DDD) is a software design approach that emphasizes modeling software based on the business domain. A key concept is the Bounded Context: a clear boundary within which a particular domain model is defined and applicable. Each bounded context represents a cohesive area of business functionality with its own language, rules, and data.

Why this matters for split environments: The security sensitivity boundary should align with natural bounded context boundaries. When a bounded context is self-contained—with clear interfaces to other contexts—it can be developed, tested, and deployed independently, making it a natural candidate for environment separation based on data sensitivity.

Example:

Consider a banking application with these bounded contexts:

  • Public Product Catalog (non-sensitive): Information about banking products, rates, branch locations—no customer data
  • Account Management (sensitive): Customer accounts, transactions, balances—requires high security

These contexts have clear boundaries and communicate through well-defined APIs. The Product Catalog can be developed in a less restrictive environment, while Account Management stays in the secure environment. Each can evolve independently as long as their contract remains stable.

Key principles:

  • Each bounded context should be owned by a single team in a single environment
  • Contexts communicate through explicit contracts (APIs, events, messages)
  • The security sensitivity split should follow bounded context boundaries, not cut across them

Anti-pattern: Splitting a single bounded context across environments creates artificial technical boundaries where natural domain boundaries should exist, forcing unnecessary coordination and complexity.

Testing Strategy

In split environments, the complete software is only assembled at the end of the delivery chain, in the more restrictive environment. Consequently, the most representative tests—full system, black-box testing on the final target—can only run at this final stage.

However, discovering defects late in the delivery process is costly. The later a defect is found, the more expensive it is to fix—requiring more investigation, rework, and coordination across teams and environments. This principle, known as the Cost of Change curve , motivates “shift-left ” approaches: moving testing as early as possible in the development lifecycle.

Building a testing strategy that enables early validation—starting in the less restrictive environment—is therefore critical to maintain fast feedback loops and reduce correction costs.

Two key concepts support this early testing approach:

  • Test pyramid : Emphasizes building a solid foundation of fast, isolated tests (unit and integration) that catch most defects early, reserving slower end-to-end tests for final validation
  • Continuous Integration : Automates test execution on every code change, providing immediate feedback to developers and preventing defect accumulation

In practice, this means applying the test pyramid at each stage of the delivery chain:

Tests pyramids

In the less restrictive environment, the team developing non-sensitive components runs the full pyramid locally:

  • Unit and integration tests execute in CI pipelines on every commit (base of the pyramid)
  • The component is deployed to a dedicated platform for end-to-end testing (top of the pyramid)

In the more restrictive environment, when the component is delivered, the same strategy applies:

  • The receiving team runs their own test pyramid, from unit tests up to integration tests
  • Final black-box tests validate the complete system against customer requirements

This approach ensures that defects are caught as early as possible in each environment, while the final validation confirms the assembled product meets its specifications.

Warning

While early testing is essential, do not postpone end-to-end validation on the final target until the very end. Aim to execute the complete delivery chain as early as possible to verify overall coherence.

For example, if you build test doubles or mocks to simulate dependencies, validate their compatibility with the real target environment early—otherwise, you risk significant rework when integration finally happens.

However, this ideal scenario must be adapted based on the nature of the software developed in the less restrictive environment. Depending on the component’s characteristics, it may not be possible to run all test types—particularly end-to-end tests if the component cannot be deployed or executed in isolation.

This is where software architecture becomes critical: a component designed with clear interfaces, dependency injection, and proper separation of concerns is inherently more testable. Without these architectural properties, teams lose the ability to validate their work independently, pushing defect detection later in the delivery chain.

To simulate dependencies that are unavailable in the less restrictive environment, teams can rely on test doubles and contract testing —techniques that require well-designed interfaces to be effective.

Note

Certification context (e.g., DO-178 for avionics): In certification-driven development, only tests executed on the final target with the complete system count toward qualification. Tests run earlier with stubs provide no certification credit, which may seem like wasted effort.

However, the cost of finding defects late still applies—arguably even more so given certification rework costs. The key is to design tests for reuse: the same test cases run early with stubs for fast feedback, then run again on the final target for qualification. This way, early testing investment directly contributes to certification.

Build & Delivery

Each team must operate a complete, autonomous CI/CD pipeline that handles the full lifecycle: build, test, code quality analysis, security scanning, and delivery. This autonomy enables fast feedback and independent deployment.

However, pipeline designs across environments must be compatible and ideally share common patterns. Divergent pipeline architectures create friction when components need to integrate, complicate troubleshooting, and increase the overall system’s cognitive load.

Continuous Integration Per Environment

Each environment maintains its own complete CI/CD pipeline, following DORA’s continuous delivery capability . While teams operate these pipelines autonomously, the pipelines serve different but complementary purposes.

CI Pipelines

The less restrictive environment focuses on building and validating non-sensitive components in an optimal development environment. The pipeline builds components, runs all test levels (unit, integration, contract), performs code quality and security analysis, then publishes the validated output for delivery.

The more restrictive environment focuses on assembling and validating the complete system. The pipeline consumes non-sensitive components (as artifacts or code), builds sensitive components, performs full system integration testing, conducts final security validation, and generates the deployable product.

Pipeline Architecture Coherence

While each team operates independently, pipeline architectures must maintain coherence to prevent integration issues and operational complexity.

Common patterns and tooling: Use consistent CI/CD platforms, build tools, artifact formats, and delivery mechanisms across environments. This doesn’t mean identical pipelines, but compatible approaches.

Platform capabilities alignment: The pipeline design must account for platform capabilities in each environment. Some environments may have restricted tooling or limited resources.

Example: If the more restrictive environment cannot build container images due to security restrictions or tooling limitations, the less restrictive environment must build these images, scan them for security vulnerabilities, and deliver them as signed artifacts to the restrictive environment. The pipeline architecture must explicitly design for this capability gap.

Facilitating coherence: Two approaches significantly reduce the burden of maintaining coherent pipelines:

  • Platform team: A platform team provides standardized pipeline templates and patterns, manages shared tooling and infrastructure, and abstracts environment-specific complexity.
  • InnerSource pipeline components: GitLab CI/CD steps and templates shared as InnerSource components—reusable, version-controlled pipeline building blocks maintained collaboratively across teams. These naturally promote consistency since teams use the same tested components rather than reimplementing pipeline logic in each environment. See NextGen-CICD step or Umbrella .

Delivery Approach

Non-sensitive components are developed in the less restrictive environment, then delivered to the more restrictive environment where the final product is assembled.

Artifact-Based Delivery

Build once in the less restrictive environment, deliver binary artifacts to the more restrictive environment.

flowchart LR
    A[" Code Source<br>🔓Less restrictive"]
    B[" Artifacts Repository<br>🔓 Less restrictive"]
    C[[" Artifact Repository<br>🔒 More restrictive"]]
    
    A -->|build 📦| B
    B --> |Mirrored 📦|C

This follows the Continuous Delivery principle of building immutable artifacts that progress through environments without recompilation.

Delivery mechanisms:

Benefits:

  • Non-sensitive code is compiled and tested in optimal environment
  • Only proven, immutable artifacts cross the security boundary
  • Faster integration in restrictive environment (no recompilation)
Code-Based Delivery

Deliver source code from the less restrictive environment to the more restrictive environment, where non-sensitive code is built alongside sensitive code.

flowchart LR
    A[" Code Repository<br>🔓Less restrictive"]
    B[[" Code Repository<br>🔒More restrictive"]]
    C[[" Artifact Repository<br>🔒More restrictive"]]
    
    A -->|📄| B
    B --> |build 📦|C
  • GitLab repository mirroring : Automatically synchronize repositories between environments
  • Git submodules : Reference non-sensitive repositories as submodules in the restrictive environment
  • Git subrepo : Incorporate non-sensitive code into the restrictive environment repository
Choosing Between Code-Based and Artifact-Based Delivery
Important

Artifact-Based Delivery is recommended as the default approach when feasible, as it aligns with Continuous Delivery best practices and DORA capabilities .

Choose Artifact-Based when:

  • Architecture maturity: Loosely coupled components with stable interfaces and clear version contracts
  • Tooling constraints: Restrictive environment has limited build capabilities or older tool versions
  • Security by immutability: Need for strong artifact traceability (versioning, signatures, checksums, audit trail)
  • DevOps maturity: Teams have robust automated testing and artifact registry infrastructure

Choose Code-Based when:

  • Build coupling: Components have build-time dependencies or require compilation together for ABI compatibility
  • Security policies: Restrictive environment requires “build from source” for certification or prohibits external pre-compiled binaries
  • Infrastructure simplicity: Artifact registry not available or team prefers simpler Git-only workflow
  • Code nature: Interpreted languages (Python, JavaScript) or infrastructure-as-code where source code is the deliverable

Mixed approach: Use artifact-based delivery for stable, widely-used libraries and code-based delivery for frequently changing application code.

Note

If starting with a monolith and strong coupling, code-based delivery may be more pragmatic initially. As architecture evolves toward looser coupling, transition to artifact-based delivery for better alignment with Continuous Delivery principles.

Special case - Code Forking with bidirectional synchronization

When access constraints force a split despite strong architectural coupling (e.g., a monolith where some team members cannot access the restrictive environment), use code forking to maintain team autonomy without environment switching.

Each team works on a fork of the entire codebase in their own environment:

  • Automatic sync (less restrictive → more restrictive):
  • Manual sync (more restrictive → less restrictive): Explicit merge requests to selectively control what flows back, maintaining security controls
flowchart LR
    A[" Code Repository<br>🔓Less restrictive"]
    B[[" Code Repository<br>🔒More restrictive"]]
    C[[" Artifact Repository<br>🔒More restrictive"]]
    
    A -->|📄automatic| B
    B -->|📄manual| A 
    B --> |build 📦|C

This approach enables the more restricted team to remain fully autonomous—developing, testing, and integrating without waiting for synchronizations or switching environments.

Warning

Code forking has significant costs: merge conflicts when both teams modify shared code, manual synchronization overhead, and risk of code divergence. Only use when access constraints make it unavoidable. The preferred long-term strategy is to refactor toward loose coupling to enable standard split environment patterns.

Several Git mechanisms can implement the fork pattern, each with a different synchronization model:

  • True repository fork

    A full clone of the repository lives in each environment. Each team works entirely within their own copy using standard Git workflows. This works well when neither side needs direct access to the other’s source code. If cross-environment source sharing is required, it must be handled through another mechanism.
    Then both teams modify shared code, conflicts must be resolved explicitly via merge or rebase, requiring a disciplined and regular sync process to prevent history from diverging.

  • Git submodule

    The restrictive environment references the less-restrictive repository as a [Git submodule] inside an existing repository. Importantly, the parent repository does not track the submodule’s branch: it pins a specific commit SHA of the submodule. When a change is pushed to the submodule repository, the parent repository must be explicitly updated to reference the new SHA—otherwise the parent continues to use the previous version. Developers can commit changes directly in the submodule and push them back to the source repository. The drawback is operational: to push a change back, the developer needs access to both environments simultaneously—managing credentials, pipelines, and workflows on both sides—compounding the cognitive load that split environments are supposed to reduce.

  • Git subrepo

    Git subrepo is a middle ground between a fork and a submodule: it embeds upstream content directly in the repository (like a subtree) while retaining metadata to enable future synchronization (like a submodule).
    pull and push are independent commands, but their behavior regarding commit history is not strictly symmetric. Squashing may be applied differently depending on the direction, which can complicate conflict resolution and history traceability.

  • Git subtree merge strategy

    A lower-level approach that uses Git’s built-in subtree merge strategy to synchronize a subdirectory with an external branch, without requiring additional tooling:

    git merge -Xsubtree=<subdirectory> <remote>/<branch> --allow-unrelated-histories
    

    This merges a remote branch into a specific subdirectory of the current repository, treating it as a subtree. It enables explicit, controlled synchronization with no external tooling dependency. See the git merge -Xsubtree documentation.

Warning

Regardless of the mechanism chosen, the same person or team must coordinate changes across both sides of the boundary. This is the fundamental limitation of code forking: it trades environment switching for synchronization overhead.

Summary: Delivery Approaches Comparison
ApproachWhat flows between environmentsWhen to useKey trade-off
Artifact-Based (recommended)Binary artifacts (JARs, containers, packages)Loosely coupled architecture, mature CI/CD, need for immutabilityBest practice alignment vs. requires artifact registry infrastructure
Code-BasedSource codeBuild coupling, “build from source” policies, interpreted languagesSimpler infrastructure vs. rebuilding in restrictive environment
Code Forking (last resort)Bidirectional code sync (automatic→ / manual←)Strong coupling + access constraints force splitTeam autonomy maintained vs. merge conflicts and divergence risk

References

  • Conway’s Law : Conway, M. (1968). “How Do Committees Invent?”
  • Domain-Driven Design : Evans, E. (2003). “Domain-Driven Design: Tackling Complexity in the Heart of Software”
  • DORA : DevOps Research and Assessment program researching software delivery performance
  • Team Topologies : Skelton, M. & Pais, M. (2019). “Team Topologies: Organizing Business and Technology Teams for Fast Flow”