<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John Reilly Pospos (JP)</title>
    <description>The latest articles on DEV Community by John Reilly Pospos (JP) (@severity1).</description>
    <link>https://dev.to/severity1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1944425%2F0d63b186-fa64-490a-ad2f-e586766f1d2b.jpg</url>
      <title>DEV Community: John Reilly Pospos (JP)</title>
      <link>https://dev.to/severity1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/severity1"/>
    <language>en</language>
    <item>
      <title>The Curious Case of Terraform Workspaces</title>
      <dc:creator>John Reilly Pospos (JP)</dc:creator>
      <pubDate>Wed, 12 Nov 2025 00:57:40 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-curious-case-of-terraform-workspaces-3llo</link>
      <guid>https://dev.to/aws-builders/the-curious-case-of-terraform-workspaces-3llo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Why everyone thinks Terraform workspaces are bad, what the docs actually say, and how to use them properly. This is NOT a 'workspaces are supreme, use them for everything' post.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A master programmer was asked: “What makes code elegant?” He replied: “When you remove what is not essential, what remains is truth.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you’ve spent any time in the Terraform community, you’ve likely encountered the heated debate around workspaces. Many developers read HashiCorp’s documentation and immediately conclude that Terraform workspaces should be avoided. But is this really what the documentation says? Or have we collectively misunderstood the nuanced guidance HashiCorp provides?&lt;/p&gt;

&lt;p&gt;I used to be in the “workspaces are evil” camp myself. I’d built wrappers, used off-the-shelf abstraction layers, anything to avoid workspaces. Then I started working with larger teams and more complex deployments, and I began to see a different perspective. What if the guidance isn’t “never use workspaces,” but something more nuanced?&lt;/p&gt;

&lt;p&gt;Sometimes the simplest solution is right in front of you.&lt;/p&gt;

&lt;p&gt;Based on my experience and interpretation of the documentation, let’s dive deep into what Terraform workspaces actually are, what the documentation really says, and how to use them effectively as part of a broader infrastructure-as-code strategy that includes proper system decomposition and environment management.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Concepts
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The ancient Unix wisdom teaches: “Understand your abstractions, or they will abstract you.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before we dive into the workspace debate, let’s establish a crucial concept that’s central to understanding the proper use of workspaces: &lt;strong&gt;composition layers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Composition layers&lt;/strong&gt; are logical groupings of related infrastructure components that naturally belong together and share similar lifecycle characteristics. They represent the fundamental architectural boundaries in your infrastructure-as-code approach.&lt;/p&gt;

&lt;p&gt;Examples of composition layers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Network layer:&lt;/strong&gt; VPCs, subnets, routing tables, NAT gateways, security groups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage layer:&lt;/strong&gt; RDS databases, S3 buckets, EFS file systems, backup policies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute layer:&lt;/strong&gt; EC2 instances, Auto Scaling groups, Load Balancers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application layer:&lt;/strong&gt; Specific applications or services with their dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key principle is that components within a composition layer typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change at similar frequencies and for similar reasons&lt;/li&gt;
&lt;li&gt;Have similar operational requirements and ownership&lt;/li&gt;
&lt;li&gt;Share blast radius considerations (if one breaks, what else might be affected?)&lt;/li&gt;
&lt;li&gt;Benefit from being deployed and managed together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, your VPC structure rarely changes, but your application deployments happen frequently. These belong in separate composition layers because they have different lifecycle needs.&lt;/p&gt;

&lt;p&gt;This architectural pattern directly addresses many of the concerns raised about Terraform workspaces, as we’ll see throughout this post.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Great Misunderstanding
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A novice asked the master: “I have read the first page of the manual. Am I now wise?” The master replied: “A man who stops reading after the first page will spend his life debugging the last page.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The infamous documentation section that causes so much confusion states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Important: Workspaces are not appropriate for system decomposition or deployments requiring separate credentials and access controls. Refer to Use Cases in the Terraform CLI documentation for details and recommended alternatives.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many readers stop here and declare workspaces off-limits. But this interpretation might be missing some context. One way to read this is as guidance to not use workspaces as your only strategy for managing complex multi-environment deployments.&lt;/p&gt;

&lt;p&gt;The community’s concerns about workspaces are valid. System decomposition, credential isolation, and blast radius control are critical challenges. The question is whether workspaces can be part of the solution when these other concerns are properly addressed.&lt;/p&gt;

&lt;p&gt;But here’s what the docs actually recommend workspaces for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Workspaces are convenient because they let you create different sets of infrastructure with the same working copy of your configuration and the same plugin and module caches.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So which is it? Are workspaces problematic or convenient?&lt;/p&gt;




&lt;h2&gt;
  
  
  What Terraform Workspaces Actually Are
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A student asked: “What is a workspace?” The master replied: “The same house with different keys to different rooms.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At their core, Terraform workspaces are simply a way to manage multiple state files within a single configuration.&lt;/p&gt;

&lt;p&gt;According to the official documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“The persistent data stored in the backend belongs to a workspace. The backend initially has only one workspace containing one Terraform state associated with that configuration. Some backends support multiple named workspaces, allowing multiple states to be associated with a single configuration.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The important distinction is that workspaces let you switch between different states &lt;strong&gt;within the same configuration&lt;/strong&gt;. You’re not duplicating your Terraform configuration files. Instead, you get isolated state contexts that all use the same infrastructure code, allowing you to deploy the same configuration to different environments or test different variations safely.&lt;/p&gt;

&lt;p&gt;For example, within your &lt;code&gt;network&lt;/code&gt; composition layer, you might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;network-prod&lt;/strong&gt; workspace (production VPC and subnets)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;network-dev&lt;/strong&gt; workspace (smaller dev environment VPC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;network-feature-zones&lt;/strong&gt; workspace (testing additional availability zones)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All use the same Terraform configuration, but maintain completely separate state files.&lt;/p&gt;

&lt;p&gt;They’re a built-in feature that allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switch between different states using &lt;code&gt;terraform workspace select
&lt;/code&gt;- Maintain separate state files for the same composition layer configuration&lt;/li&gt;
&lt;li&gt;Reference the current workspace name in your configuration using &lt;code&gt;${terraform.workspace}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Deploy multiple instances of the same composition layer safely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it. No magic, no complex orchestration, no additional code to maintain. Just state file management with a convenient logical interface within composition layer boundaries. So why do so many developers avoid this simple, built-in feature?&lt;/p&gt;




&lt;h2&gt;
  
  
  The Statements That Scare People Away
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;An infrastructure architect reflected: “Tools are not good or bad. They are appropriate or inappropriate. Wisdom lies in knowing the difference.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we understand that workspaces actually manage multiple states within a composition layer, let’s examine what the documentation says about their appropriate usage.&lt;/p&gt;

&lt;p&gt;Here are the statements that cause the most concern:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Workspaces are not appropriate for system decomposition or deployments requiring separate credentials and access controls.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“CLI workspaces use the same backend, so they are not a suitable isolation mechanism for this scenario.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Workspaces alone are not a suitable tool for system decomposition because each subsystem should have its own separate configuration and backend.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Workspaces can be helpful for specific use cases, but they are not required to use the Terraform CLI. We recommend using alternative approaches for complex deployments requiring separate credentials and access controls.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Instead of creating CLI workspaces, you can use one or more re-usable modules to represent the common elements and then represent each instance as a separate configuration that instantiates those common elements in the context of a different backend.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are all valid concerns. System decomposition, credential isolation, team boundaries, deployment complexity, and code reusability are critical challenges in infrastructure management. The crucial point is that &lt;strong&gt;these are concerns that workspaces are NOT meant to solve!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are other mechanisms to address these challenges. These include separate backends, different IAM roles, composition layer boundaries, reusable modules, remote state data sources, and orchestration tools. The question then becomes: what does the documentation actually recommend as the complete solution?&lt;/p&gt;




&lt;h2&gt;
  
  
  What The Documentation Actually Recommends
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A student complained: “These tools are so complex!” The Unix master replied: “Complexity is what happens when you refuse to understand simplicity.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s what many people miss: the documentation doesn’t just tell you what workspaces aren’t good for, it also describes what you should do instead. When you read these statements together, they outline a complete architectural approach.&lt;/p&gt;

&lt;p&gt;The documentation’s recommended approach includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;System decomposition FIRST:&lt;/strong&gt; Break infrastructure into logical composition layers with separate configurations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate backends:&lt;/strong&gt; Use different backends for proper team and component isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable modules:&lt;/strong&gt; Create common patterns to avoid code duplication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component communication:&lt;/strong&gt; Use “paired resources and data sources” for composition layers to communicate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alternative deployment strategies:&lt;/strong&gt; Consider separate configurations over workspace-only approaches&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The critical insight is in this statement:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Instead of creating CLI workspaces, you can use one or more re-usable modules to represent the common elements and then represent each instance as a separate configuration that instantiates those common elements in the context of a different backend.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This isn’t saying “never use workspaces.” It’s describing the &lt;strong&gt;complete architecture&lt;/strong&gt; that addresses all the concerns raised. The question becomes: where do workspaces fit within this recommended architecture?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The answer:&lt;/strong&gt; Workspaces work safely &lt;strong&gt;within each properly isolated composition layer&lt;/strong&gt; for managing different environments or feature branches of that same layer, once you have the proper foundation in place.&lt;/p&gt;

&lt;p&gt;This interpretation suggests the documentation is actually describing a comprehensive approach where workspaces have their proper place as part of the solution, not as the entire solution.&lt;/p&gt;

&lt;p&gt;Now let’s walk through how to implement this complete approach:&lt;/p&gt;




&lt;h2&gt;
  
  
  Building The Recommended Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: System Decomposition FIRST
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The wise system builder said: “First, architect your components. Then, let the tools serve the architecture. Never let tools dictate the architecture.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The documentation emphasizes that workspaces are not appropriate for system decomposition, so this is where we address that concern &lt;strong&gt;before&lt;/strong&gt; workspaces even come into play.&lt;/p&gt;

&lt;p&gt;The first step is breaking your infrastructure into logical composition layers that make sense for your context. This could be based on your organization’s responsibility model, blast radius control, lifecycle velocities, or whatever boundaries work best for your team.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Consider separating storage and stateful resources from compute and networking. Persistent resources like RDS databases, S3 buckets, and EFS file systems often have different lifecycle needs. They tend to change less frequently, require careful migration strategies, and often need to persist even when applications are rebuilt. You may find that mixing them with faster-changing compute resources creates operational complexity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Co-located Structure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;infrastructure/
├── network/                        &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   ├── dev.tfvars
│   ├── staging.tfvars
│   └── prod.tfvars
├── storage/                        &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   ├── dev.tfvars
│   ├── staging.tfvars
│   └── prod.tfvars
└── workload-a/                     &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
    ├── main.tf
    ├── variables.tf
    ├── outputs.tf
    ├── dev.tfvars
    ├── staging.tfvars
    └── prod.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Centralized Config Structure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;infrastructure/
├── composition-layers/
│   ├── network/                    &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── storage/                    &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── workload-a/                 &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── config/
    ├── network/
    │   ├── dev.tfvars
    │   ├── staging.tfvars
    │   └── prod.tfvars
    ├── storage/
    │   ├── dev.tfvars
    │   ├── staging.tfvars
    │   └── prod.tfvars
    └── workload-a/
        ├── dev.tfvars
        ├── staging.tfvars
        └── prod.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 3: Local Config Structure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;infrastructure/
├── network/                        &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
│   ├── terraform/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── config/
│       ├── dev.tfvars
│       ├── staging.tfvars
│       └── prod.tfvars
├── storage/                        &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
│   ├── terraform/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── config/
│       ├── dev.tfvars
│       ├── staging.tfvars
│       └── prod.tfvars
└── workload-a/                     &lt;span class="c"&gt;# Could be its own repo&lt;/span&gt;
    ├── terraform/
    │   ├── main.tf
    │   ├── variables.tf
    │   └── outputs.tf
    └── config/
        ├── dev.tfvars
        ├── staging.tfvars
        └── prod.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This decomposition directly addresses the workspace concerns we discussed earlier. By properly separating composition layers, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System decomposition solved:&lt;/strong&gt; Each composition layer has clear boundaries and responsibilities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential isolation ready:&lt;/strong&gt; Different teams can manage different composition layers with separate access controls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blast radius control:&lt;/strong&gt; Changes to &lt;code&gt;workload-a&lt;/code&gt; don’t risk breaking the network or corrupting databases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Independent deployment cycles:&lt;/strong&gt; Each composition layer can be deployed separately based on its change frequency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State isolation:&lt;/strong&gt; Each composition layer has its own state file, preventing accidental dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational safety:&lt;/strong&gt; You can rebuild applications without touching persistent data stores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team boundaries:&lt;/strong&gt; The network team manages &lt;code&gt;network/&lt;/code&gt;, the storage team manages &lt;code&gt;storage/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Infrastructure shifts and ebbs like the Sahara. You will be buried in sand if you stand still. What works for a 5-person startup won’t work for a 500-person enterprise. Be prepared to evolve your structure as your team and infrastructure mature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This decomposition addresses the fundamental workspace concerns by establishing proper architectural boundaries. With these foundations in place, we can now add secure credential isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Separate Backends for Team Isolation
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A security engineer asked: “How do I know my access controls work?” The master replied: “When teams work in one garden - each tends their own plot, but all may harvest what grows well.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The documentation states that “CLI workspaces use the same backend, so they are not a suitable isolation mechanism” for team separation. This is where our composition layer approach directly addresses the concern.&lt;/p&gt;

&lt;p&gt;The advantage of our system decomposition is that since we’ve properly separated our infrastructure into distinct composition layers, we naturally gain the capability for credential isolation, regardless of whether we use workspaces or not. Each composition layer can have its own dedicated backend with different IAM roles and access controls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Network Composition Layer Backend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# network/backend.tf&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mycompany-terraform-network-state"&lt;/span&gt;  &lt;span class="c1"&gt;# Dedicated bucket for network composition layer&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;use_lockfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                                  &lt;span class="c1"&gt;# State locking via S3 object locking&lt;/span&gt;

    &lt;span class="nx"&gt;assume_role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/NetworkTeamRole"&lt;/span&gt;  &lt;span class="c1"&gt;# Only network team has access&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Application Composition Layer Backend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# workload-a/backend.tf  &lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mycompany-terraform-workload-a-state"&lt;/span&gt;  &lt;span class="c1"&gt;# Dedicated bucket for workload-a composition layer&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;use_lockfile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;                                      &lt;span class="c1"&gt;# State locking via S3 object locking&lt;/span&gt;

    &lt;span class="nx"&gt;assume_role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/AppTeamRole"&lt;/span&gt;  &lt;span class="c1"&gt;# Only app team has access&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now each team has proper credential isolation with dedicated backends and credentials. The network team can’t access the application team’s state, and vice versa. &lt;strong&gt;This is where workspaces become safe and appropriate.&lt;/strong&gt; They work within each properly isolated composition layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advanced Security: Composition Layer + Environment Isolation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For maximum security (perfect for highly regulated environments), you can use dedicated backends per composition layer AND environment-specific access policies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Network Composition Layer Bucket Policy&lt;/strong&gt; (on &lt;code&gt;mycompany-terraform-network-state&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"Version"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"Statement"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"Sid"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"NetworkDevTeamAccess"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"Effect"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"Principal"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"AWS"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/NetworkDevRole"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="s2"&gt;"Action"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:DeleteObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s2"&gt;"Resource"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:s3:::mycompany-terraform-network-state/*"&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s2"&gt;"Condition"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"StringLike"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"s3:prefix"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"terraform.tfstate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"env:/network-dev/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"env:/network-feature-*/*"&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"Sid"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"NetworkProdTeamAccess"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="s2"&gt;"Effect"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"Principal"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"AWS"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/NetworkProdRole"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="s2"&gt;"Action"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:DeleteObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s2"&gt;"Resource"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:s3:::mycompany-terraform-network-state/*"&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s2"&gt;"Condition"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"StringLike"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"s3:prefix"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"env:/network-prod/*"&lt;/span&gt;
          &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Application Composition Layer Bucket Policy&lt;/strong&gt; (on &lt;code&gt;mycompany-terraform-workload-a-state&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"Version"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="s2"&gt;"Statement"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"Sid"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"AppTeamAccess"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"Effect"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"Principal"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"AWS"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/AppTeamRole"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="s2"&gt;"Action"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:PutObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:DeleteObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="s2"&gt;"Resource"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:s3:::mycompany-terraform-workload-a-state/*"&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you multiple security layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Backend-level isolation&lt;/strong&gt; - Network team can’t even see app team’s backend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment-level restrictions&lt;/strong&gt; - Dev team can’t touch prod state files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition layer boundaries&lt;/strong&gt; - Complete separation between infrastructure composition layers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace safety&lt;/strong&gt; - Feature branches are isolated within appropriate environments&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This example uses the S3 backend with IAM role-based isolation within a single AWS account, which is common for self-managed Terraform. For even stronger isolation, many organizations prefer separate AWS accounts for different environments or teams, providing complete billing, resource, and access boundaries. If you’re using HCP Terraform with the remote backend, workspace configuration works differently. In that case, you would use workspaces.prefix to map CLI workspaces to remote workspaces. For example, prefix = "network-" maps terraform workspace select prod to the network-prod remote workspace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With secure backend isolation established, we’ve addressed the credential separation concern. Now we can ensure our composition layers don’t become repetitive code maintenance burdens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Reusable Modules
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The DRY principle master said: “Write once, use many times. But abstract wrongly, debug infinite times.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The documentation recommends using “one or more re-usable modules to represent the common elements” as part of the complete solution. This addresses the code duplication concern while working alongside our composition layer architecture.&lt;/p&gt;

&lt;p&gt;This approach isn’t replacing workspaces; it’s providing the reusable foundation that makes workspaces safe and effective within each composition layer.&lt;/p&gt;

&lt;p&gt;Modules provide reusable infrastructure patterns within our composition layer architecture. They work alongside the foundation we’ve built, not competing with it, but making it more maintainable. Here’s how:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VPC Module Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# modules/vpc/main.tf&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_support&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_dns_support&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-vpc"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"name_prefix"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Prefix for resource names"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This module encapsulates VPC creation with configurable parameters. It’s reusable across different composition layers and environments without duplicating code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using the Module:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# network/main.tf&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"../modules/vpc"&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;name_prefix&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The network composition layer uses this module, passing in environment-specific values. The same module can be used across different composition layers or environments with different configurations.&lt;/p&gt;

&lt;p&gt;With reusable modules in place, we’ve eliminated code duplication while maintaining our composition layer boundaries. Now we can address how these separate layers communicate with each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Composition Layer Communication
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The distributed systems sage declared: “Loose coupling with high cohesion: this is the way of maintainable systems.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The documentation provides specific guidance on how separate composition layers should communicate: “When multiple configurations represent distinct system components rather than multiple deployments, you can pass data from one component to another using paired resources types and data sources.”&lt;/p&gt;

&lt;p&gt;This directly addresses our need to connect our distinct composition layers. Here’s how the recommended “paired resources and data sources” pattern works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approach 1: Paired Resource Outputs and Remote State Data Sources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Network component exposes data via resource outputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# network/outputs.tf&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"private_subnet_ids"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnet_ids&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Application component consumes data via terraform_remote_state data source:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# workload-a/main.tf&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"terraform_remote_state"&lt;/span&gt; &lt;span class="s2"&gt;"network"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mycompany-terraform-network-state"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

    &lt;span class="nx"&gt;assume_role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::123456789012:role/NetworkTeamRole"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amazon_linux&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terraform_remote_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnet_ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Approach 2: Paired Resource Tags and Named Data Sources&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# workload-a/main.tf&lt;/span&gt;
&lt;span class="c1"&gt;# Instead of remote state, use data sources to discover resources&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tag:Name"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-vpc"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnets"&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vpc-id"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tag:Type"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amazon_linux&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_subnets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both approaches implement the “paired resources and data sources” pattern the documentation describes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Approach 1:&lt;/strong&gt; Explicit pairing through outputs and remote state creates clear dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approach 2:&lt;/strong&gt; Implicit pairing through resource tags and named data sources provides looser coupling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both maintain composition layer boundaries while enabling the necessary communication between distinct system components.&lt;/p&gt;

&lt;p&gt;With composition layer communication established, we have the complete foundation in place. Now we can finally show where workspaces fit within this properly architected system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Where Workspaces Finally Fit
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A configuration management wizard observed: “The same code in different environments should behave predictably different.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have the complete foundation in place, we can finally show workspaces in their proper context. Remember the documentation’s guidance: workspaces are for creating “a parallel, distinct copy of a set of infrastructure in order to test a set of changes before modifying the main production infrastructure.”&lt;/p&gt;

&lt;p&gt;Within each properly isolated composition layer, workspaces provide exactly that. They offer a safe way to test variations. To handle environment-specific configuration differences, tfvars files provide the solution. This combination gives you a clean separation between your infrastructure code and environment-specific configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why tfvars + workspaces work so well together:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Same code, different configs:&lt;/strong&gt; Workspaces let you deploy identical Terraform code with different configurations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment isolation:&lt;/strong&gt; Each workspace gets its own state, but can use different tfvars for sizing, features, and settings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration outside code:&lt;/strong&gt; tfvars keep environment-specific details out of your .tf files, making them truly reusable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe testing:&lt;/strong&gt; Feature workspaces can use dev.tfvars for smaller, cheaper resources while prod uses prod.tfvars&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean workflows:&lt;/strong&gt; Switch workspace, apply with appropriate tfvars for simple and predictable operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how this elegant combination works in practice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment-Specific tfvars:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# workload-a/config/dev.tfvars&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
&lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;
&lt;span class="nx"&gt;min_capacity&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nx"&gt;max_capacity&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="nx"&gt;enable_monitoring&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="c1"&gt;# workload-a/config/prod.tfvars  &lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;
&lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.large"&lt;/span&gt;
&lt;span class="nx"&gt;min_capacity&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="nx"&gt;max_capacity&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="nx"&gt;enable_monitoring&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Clean Resource Configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# workload-a/main.tf&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_autoscaling_group"&lt;/span&gt; &lt;span class="s2"&gt;"app"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app-asg-&lt;/span&gt;&lt;span class="k"&gt;${terraform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;min_size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;min_capacity&lt;/span&gt;
  &lt;span class="nx"&gt;max_size&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max_capacity&lt;/span&gt;
  &lt;span class="nx"&gt;desired_capacity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;min_capacity&lt;/span&gt;

  &lt;span class="c1"&gt;# Use network composition layer's outputs&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_zone_identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;terraform_remote_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnet_ids&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Complete Workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;workload-a/

&lt;span class="c"&gt;# 1. Create feature branch environment for development&lt;/span&gt;
terraform workspace new workload-a-feature-user-dashboard
terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config/dev.tfvars  &lt;span class="c"&gt;# Test with smaller, cheaper resources&lt;/span&gt;

&lt;span class="c"&gt;# 2. Test in shared dev environment&lt;/span&gt;
terraform workspace &lt;span class="k"&gt;select &lt;/span&gt;workload-a-dev
terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config/dev.tfvars  &lt;span class="c"&gt;# Shared dev environment&lt;/span&gt;

&lt;span class="c"&gt;# 3. Deploy to production after testing&lt;/span&gt;
terraform workspace &lt;span class="k"&gt;select &lt;/span&gt;workload-a-prod
terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config/prod.tfvars  &lt;span class="c"&gt;# Full production resources&lt;/span&gt;

&lt;span class="c"&gt;# 4. Clean up feature environment&lt;/span&gt;
terraform workspace &lt;span class="k"&gt;select &lt;/span&gt;workload-a-feature-user-dashboard
terraform destroy &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config/dev.tfvars
terraform workspace &lt;span class="k"&gt;select &lt;/span&gt;workload-a-dev  &lt;span class="c"&gt;# Switch to any other workspace&lt;/span&gt;
terraform workspace delete workload-a-feature-user-dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This demonstrates the workspace documentation’s guidance. It creates “parallel, distinct copies” to test changes safely before affecting production infrastructure. The tfvars and workspace combination provides the perfect interface with the same infrastructure code, different configurations, and isolated state.&lt;/p&gt;

&lt;p&gt;Notice how we never modified the Terraform code itself. We just switched workspaces and applied different configurations. This is the elegant simplicity the documentation describes.&lt;/p&gt;

&lt;p&gt;This workflow is both scalable and portable. The same commands work whether you’re running them locally on your laptop, in a GitHub Actions pipeline, GitLab CI, Jenkins, or any other CI/CD system. No special orchestration tools or complex deployment scripts required. Just standard Terraform commands that work everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Unix philosophy teaches: “Do one thing, do it well, and compose with others.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This approach addresses every concern the documentation raises because it implements the complete architecture HashiCorp describes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System Decomposition FIRST:&lt;/strong&gt; Each composition layer has its own configuration and backend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate Backends:&lt;/strong&gt; Dedicated backends with composition layer-specific credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable Modules:&lt;/strong&gt; VPC module demonstrates DRY infrastructure patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition Layer Communication:&lt;/strong&gt; Remote state enables data sharing between composition layers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team Boundaries:&lt;/strong&gt; Different teams manage different composition layers with appropriate access controls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blast Radius Control:&lt;/strong&gt; Changes are isolated within composition layer boundaries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple Deployment Interface:&lt;/strong&gt; Workspaces + tfvars provide elegant environment management without complex orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspaces Within Layers:&lt;/strong&gt; Safe testing environments within proper boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Workspaces work safely within each properly isolated composition layer for testing variations and managing parallel environments, exactly as the documentation intended.&lt;/p&gt;

&lt;p&gt;The beauty of this approach is that workspaces encapsulate all the complexity of environment management within a simple, powerful interface without compromising any of the established architectural foundations. You get the benefits of proper system decomposition, security isolation, and team boundaries, while workspaces provide an elegant way to manage multiple environments within each composition layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;An infrastructure architect reflected: “Tools are not good or bad. They are appropriate or inappropriate. Wisdom lies in knowing the difference.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the documentation completely&lt;/strong&gt; - The warning is about using workspaces ALONE, not avoiding them entirely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System decomposition first&lt;/strong&gt; - Break infrastructure into logical composition layers with separate backends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use reusable modules&lt;/strong&gt; - DRY your infrastructure patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable data sharing&lt;/strong&gt; - Composition layers communicate via terraform_remote_state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate credentials&lt;/strong&gt; - Different teams, different access controls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspaces within layers&lt;/strong&gt; - Use workspaces for multiple environments and feature testing within each composition layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tfvars for configuration&lt;/strong&gt; - Keep environment-specific config out of your Terraform code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Looking back at workspace debates I’ve witnessed and participated in, I’ve come to believe the challenge wasn’t the tool itself. It was attempting to use workspaces as the primary solution for complex scenarios instead of as one part of a comprehensive infrastructure strategy.&lt;/p&gt;

&lt;p&gt;Interestingly, many teams may already have solid foundations. They have composition layer decomposition, separate backends, reusable modules, and proper credential isolation. They just need the workspaces and tfvars combination to complete the pattern.&lt;/p&gt;

&lt;p&gt;The “curious case” of Terraform workspaces isn’t that they’re bad. It’s that many of us interpreted the documentation in a way that led to avoiding workspaces entirely. When used properly as part of the complete pattern HashiCorp actually describes, they’re not just useful, they’re elegant.&lt;/p&gt;

&lt;p&gt;Sometimes the simplest solution really is right in front of you. It just requires building the proper foundation first.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Cleaner Terraform: Stop Writing Backwards Conditionals</title>
      <dc:creator>John Reilly Pospos (JP)</dc:creator>
      <pubDate>Wed, 12 Nov 2025 00:27:15 +0000</pubDate>
      <link>https://dev.to/aws-builders/cleaner-terraform-stop-writing-backwards-conditionals-5862</link>
      <guid>https://dev.to/aws-builders/cleaner-terraform-stop-writing-backwards-conditionals-5862</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How to write Terraform conditionals that read naturally and make your code easier to maintain.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“The master debugger does not ask ‘What is missing?’ but ‘What is present?’”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was tasked with refactoring an existing Terraform module. Simple enough, right? Wrong. I found myself staring at my monitor for god knows how long, trying to figure out how the logic flows in these ternary conditions. My Terraform game was a bit rusty, and I kept second-guessing myself.&lt;/p&gt;

&lt;p&gt;Was I missing something obvious? Why did this feel so backwards?&lt;/p&gt;

&lt;p&gt;Turns out the problem wasn’t my rusty skills (or maybe it was, haha). But I realized it was actually the code itself. When you write Terraform logic for things like S3 buckets, RDS databases, security groups, or EC2 instances, backwards conditionals make you think about what’s missing first. But that’s not how we naturally think about defaults:&lt;/p&gt;

&lt;p&gt;“If the value exists, use it. Otherwise, do this.”&lt;/p&gt;

&lt;p&gt;But I keep seeing Terraform code that works against this natural flow. Let’s fix that and make your code easier to read and maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Backwards Conditionals
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Backward your condition flows, hmm. flow backward your mind must, yes.” - Master Yoda, probably&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here’s a common pattern I see when setting defaults. These are &lt;strong&gt;backwards conditionals&lt;/strong&gt; because they test for absence first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This reads backwards and is confusing&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;instance_config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reads like: “If instance_type doesn’t exist, use default, otherwise use the value.” That forces you to think about absence first, which feels backwards when setting defaults.&lt;/p&gt;




&lt;h2&gt;
  
  
  Better: Forward Conditionals
&lt;/h2&gt;

&lt;p&gt;Here’s the same logic using &lt;strong&gt;forward conditionals&lt;/strong&gt; that test for presence first. This matches how we naturally think about defaults and flows in one direction without mental gymnastics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This reads naturally&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;instance_config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it reads naturally: “If instance_type exists, use it, otherwise use default.” Much better!&lt;/p&gt;

&lt;p&gt;Maybe I’m just not smart enough, but forward conditionals make peer reviews and refactoring existing code so much easier for me.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cleanest: Skip the Conditionals
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“The wise function speaks once what the conditional repeats thrice”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But we can skip the conditional debate entirely with Terraform’s &lt;code&gt;coalesce()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Crystal clear what's happening&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;instance_config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;coalesce()&lt;/code&gt; function returns the first value that isn’t null. No conditional logic needed, no forward vs backward thinking required - just clean, obvious defaults.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Examples That Matter
&lt;/h2&gt;

&lt;p&gt;These aren’t the exact modules I was refactoring, but they exhibit the same backwards conditional patterns that had me staring at my screen for god knows how long.&lt;/p&gt;

&lt;h3&gt;
  
  
  S3 Bucket Setup
&lt;/h3&gt;

&lt;p&gt;Here’s the pattern I kept seeing - backwards conditionals everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Hard to follow backwards conditionals&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"app_buckets"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buckets&lt;/span&gt;

  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default_tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
      &lt;span class="nx"&gt;Owner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"platform-team"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Better with forward conditionals&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"app_buckets"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buckets&lt;/span&gt;

  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default_tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
      &lt;span class="nx"&gt;Owner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"platform-team"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Cleanest with coalesce&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"app_buckets"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buckets&lt;/span&gt;

  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default_tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;Owner&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"platform-team"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better! No more mental gymnastics to figure out what’s happening. The clean version took me seconds to understand instead of minutes.&lt;/p&gt;




&lt;h3&gt;
  
  
  Database Configuration
&lt;/h3&gt;

&lt;p&gt;Another common pattern that shows how backwards conditionals compound. Every line forces you to think about absence first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Painful to read and debug&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_db_instance"&lt;/span&gt; &lt;span class="s2"&gt;"databases"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;

  &lt;span class="nx"&gt;identifier&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;
  &lt;span class="nx"&gt;engine&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"mysql"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;
  &lt;span class="nx"&gt;engine_version&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine_version&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"8.0"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine_version&lt;/span&gt;
  &lt;span class="nx"&gt;instance_class&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_class&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"db.t3.micro"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_class&lt;/span&gt;
  &lt;span class="nx"&gt;allocated_storage&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_size&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_size&lt;/span&gt;
  &lt;span class="nx"&gt;backup_retention_period&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backup_retention&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backup_retention&lt;/span&gt;
  &lt;span class="nx"&gt;storage_encrypted&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt_storage&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt_storage&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Forward conditionals make this much more readable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Better with forward conditionals&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_db_instance"&lt;/span&gt; &lt;span class="s2"&gt;"databases"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;

  &lt;span class="nx"&gt;identifier&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;
  &lt;span class="nx"&gt;engine&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"mysql"&lt;/span&gt;
  &lt;span class="nx"&gt;engine_version&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine_version&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine_version&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"8.0"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_class&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_class&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_class&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"db.t3.micro"&lt;/span&gt;
  &lt;span class="nx"&gt;allocated_storage&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_size&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_size&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="nx"&gt;backup_retention_period&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backup_retention&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backup_retention&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;
  &lt;span class="nx"&gt;storage_encrypted&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt_storage&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt_storage&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But coalesce is even cleaner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Cleanest with coalesce&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_db_instance"&lt;/span&gt; &lt;span class="s2"&gt;"databases"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;databases&lt;/span&gt;

  &lt;span class="nx"&gt;identifier&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;
  &lt;span class="nx"&gt;engine&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;engine_version&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"8.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;instance_class&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"db.t3.micro"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;allocated_storage&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;backup_retention_period&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backup_retention&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;storage_encrypted&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt_storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This version is so much easier to scan and understand. I can actually focus on the business logic instead of decoding the conditionals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Group Rules
&lt;/h3&gt;

&lt;p&gt;This pattern shows how backwards conditionals become a nightmare when nested. Each condition requires mental translation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Nested backwards conditions everywhere&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;security_rules&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;sg_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sg_config&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;sg_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sg_name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sg_name&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;
        &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;from_port&lt;/span&gt;  
        &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to_port&lt;/span&gt;
        &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt;
        &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;
        &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"Auto-generated rule"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Forward conditionals help, but are still verbose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Better with forward conditionals&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;security_rules&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;sg_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sg_config&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;sg_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sg_name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sg_name&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;
        &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;
        &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;  
        &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
        &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Auto-generated rule"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coalesce makes it crystal clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Cleanest with coalesce&lt;/span&gt;
&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;security_rules&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flatten&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;sg_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sg_config&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;sg_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sg_name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sg_name&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;
        &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;from_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Auto-generated rule"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can actually see what each rule is doing without getting lost in the conditional logic. This is the kind of clarity that makes refactoring so much easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Backwards Conditionals Make Sense
&lt;/h2&gt;

&lt;p&gt;Before we write off backwards conditionals entirely, there are cases where they’re actually the natural way to think about a problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Testing for intentional absence - backwards conditional feels natural here&lt;/span&gt;
&lt;span class="nx"&gt;enable_monitoring&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disable_monitoring&lt;/span&gt; &lt;span class="err"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nx"&gt;auto_backup&lt;/span&gt;       &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;skip_backup&lt;/span&gt; &lt;span class="err"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nx"&gt;apply_immediately&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maintenance_window&lt;/span&gt; &lt;span class="err"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In these cases, backwards conditionals match our thinking: “If the disable flag is missing, enable the feature.” But notice how the variable names create confusion too - &lt;code&gt;enable_monitoring&lt;/code&gt; based on &lt;code&gt;disable_monitoring&lt;/code&gt; forces you to think backwards even with good conditional logic.&lt;/p&gt;

&lt;p&gt;The key is matching your conditional pattern to how you naturally think about the problem, but also naming variables clearly.&lt;/p&gt;




&lt;h3&gt;
  
  
  When to Use What
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;coalesce()&lt;/code&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re setting simple defaults&lt;/li&gt;
&lt;li&gt;All values are the same type&lt;/li&gt;
&lt;li&gt;You want maximum readability
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coalesce&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use forward conditionals when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need more complex logic than just defaults&lt;/li&gt;
&lt;li&gt;You’re working with different data types&lt;/li&gt;
&lt;li&gt;You need conditional expressions
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Complex logic that coalesce can't handle&lt;/span&gt;
&lt;span class="nx"&gt;backup_policy&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backup_enabled&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"strict"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"standard"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use backwards conditionals when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testing for intentional absence makes semantic sense&lt;/li&gt;
&lt;li&gt;The natural thought is “if this disable flag is missing…”&lt;/li&gt;
&lt;li&gt;You’re enabling features based on missing configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid backwards conditionals for simple defaults because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They force you to think about absence first&lt;/li&gt;
&lt;li&gt;Makes your brain work harder for the common case&lt;/li&gt;
&lt;li&gt;More likely to cause bugs when refactoring&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Make Your Variables Clear Too
&lt;/h2&gt;

&lt;p&gt;Beyond conditional patterns, your variable definitions should also be clear about how nulls work. This prevents confusion before you even write the conditionals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# What does null mean here?&lt;/span&gt;
&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Map of users to create"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;permissions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;tags&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Clear defaults and what to expect&lt;/span&gt;
&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"users"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Map of IAM users to create"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# Defaults to root path&lt;/span&gt;
    &lt;span class="nx"&gt;permissions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"ReadOnly"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# Defaults to minimal access  &lt;/span&gt;
    &lt;span class="nx"&gt;tags&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;       &lt;span class="c1"&gt;# Defaults to empty tags&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Terraform 1.3+ you can set defaults right in the &lt;code&gt;optional()&lt;/code&gt; function. This means you often don’t need null checking at all!&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters for Your Team
&lt;/h2&gt;

&lt;p&gt;Clear null handling isn’t just about looking nice. It directly helps with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code Reviews:&lt;/strong&gt; Reviewers spend less time figuring out backwards conditionals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging:&lt;/strong&gt; Errors in conditional logic are easier to spot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New Team Members:&lt;/strong&gt; They understand the code faster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance:&lt;/strong&gt; Refactoring is much less error-prone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key Takeaways&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Code that reads like thought requires no translation”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use forward conditionals for defaults:&lt;/strong&gt; Think “if it exists, use it” rather than “if it’s missing, use default”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;coalesce()&lt;/code&gt;:&lt;/strong&gt; It’s the cleanest approach for simple defaults&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try &lt;code&gt;optional()&lt;/code&gt; defaults:&lt;/strong&gt; Let Terraform handle nulls for you when possible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match conditionals to thinking:&lt;/strong&gt; Choose the pattern that matches how you naturally think about the problem&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Looking back at that refactoring task, I realize the problem wasn’t my rusty Terraform skills. It was backwards conditionals that forced me to think unnaturally. Now when I write Terraform, I don’t waste time staring at conditionals trying to figure out which way the logic flows.&lt;/p&gt;

&lt;p&gt;Cleaner Terraform code means fewer bugs and happier teammates. Your future self will thank you for writing code that reads the way humans think.&lt;/p&gt;

&lt;p&gt;And as I publish this, I can’t help but think about all the backwards conditionals I’ve inflicted on the world over the years. Sorry, future me. Sorry, teammates. The horror is real, but at least now we know better… right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsj1y3wf3yphw1rzpyl5w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsj1y3wf3yphw1rzpyl5w.jpg" alt="Yoda meme about backwards logic" width="646" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>infrastructureascode</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Human-Readable vs Machine-Optimized: The Coming Split in Code Quality</title>
      <dc:creator>John Reilly Pospos (JP)</dc:creator>
      <pubDate>Wed, 12 Nov 2025 00:07:03 +0000</pubDate>
      <link>https://dev.to/severity1/human-readable-vs-machine-optimized-the-coming-split-in-code-quality-4han</link>
      <guid>https://dev.to/severity1/human-readable-vs-machine-optimized-the-coming-split-in-code-quality-4han</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Exploring how AI-assisted development might shift code quality standards from human readability to machine optimization, and what that means for the future of software development.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;What does good code look like today?&lt;/p&gt;

&lt;p&gt;Ask any senior developer and you’ll hear familiar answers: readable, maintainable, well-documented, follows established patterns, uses meaningful variable names, has clear separation of concerns. We’ve built entire movements around these principles. Clean Code, SOLID, DRY. Every language has evolved its own human-centric standards: Pythonic code, idiomatic Go, Ruby’s principle of least surprise. We conduct code reviews to enforce these standards.&lt;/p&gt;

&lt;p&gt;But here’s a question worth exploring: &lt;strong&gt;&lt;em&gt;What would good code look like if AI became reliable enough to write, test, debug, and validate most code on its own?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is purely theoretical. An educated guess based on watching how AI-generated code is evolving. I’m not an expert predicting the future. Just someone exploring what might happen if current trends continue and AI gets good enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Reliability Threshold
&lt;/h2&gt;

&lt;p&gt;The key idea behind this theory: AI reaches a point where it can write code, debug it, find root causes, and fix issues without human help. Not 60% reliable. Not 80% reliable. But reliable enough that humans shift from writing and reading code themselves to validating what the AI produces.&lt;/p&gt;

&lt;p&gt;This would be like how senior engineers review junior engineers’ work today. You check the logic and conclusions instead of doing everything from scratch. If AI shows its work (execution traces, state at each step, evidence for its findings), validation becomes more about reviewing the reasoning than reading and understanding implementation details.&lt;/p&gt;

&lt;p&gt;We’re not there yet. But if we get there, the implications are worth thinking through.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Different Set of Priorities
&lt;/h2&gt;

&lt;p&gt;Imagine a future where AI writes the code, reviews it, debugs it, and troubleshoots issues. Humans validate the AI’s work, check that behavior matches expectations, and verify outputs are correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;In that world, what defines “good” code could change.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The things that might matter most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Efficiency&lt;/li&gt;
&lt;li&gt;Behavioral correctness&lt;/li&gt;
&lt;li&gt;Output validity&lt;/li&gt;
&lt;li&gt;Token efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point needs explanation. AI systems process code as tokens, and API costs scale with token count. If AI is writing, reviewing, and analyzing code constantly (implementation, security scanning, performance checks, testing, incident response), token costs add up fast. A codebase that’s 30% smaller through shorter variable names and compact syntax could save real money at scale. When AI generates and processes code thousands of times per day but humans review it occasionally, optimizing for the frequent reader and writer makes sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why We Care About Readability Today
&lt;/h2&gt;

&lt;p&gt;We care about code readability, style guides, and clean code principles because we expect humans to write, read, understand, and modify that code.&lt;/p&gt;

&lt;p&gt;But if AI writing and reviewing its own code became standard practice, and that AI was reliable enough to trust, would human-centered readability standards still make sense? Code readability has always been a means to an end. It helps human understanding. If humans are neither the primary author nor the primary reader, the optimization target shifts.&lt;/p&gt;

&lt;p&gt;This isn’t new. We already make similar tradeoffs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nobody reads minified JavaScript. We trust source maps.&lt;/li&gt;
&lt;li&gt;Nobody reads compiled binaries. We trust debuggers.&lt;/li&gt;
&lt;li&gt;Nobody reads database internals. We trust EXPLAIN plans.&lt;/li&gt;
&lt;li&gt;Nobody hand-edits LLVM IR. We trust compilers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Code could become another intermediate representation. The source of truth becomes business requirements, test suites, and AI reasoning chains. Code itself becomes what binaries are today. Something we trust the toolchain to produce correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Beyond Code Review: What Else Changes?
&lt;/h2&gt;

&lt;p&gt;If AI handles both writing and maintaining code, and human readability becomes less important, the implications go beyond just code style.&lt;/p&gt;

&lt;p&gt;We’re already seeing early signs with AI systems like &lt;a href="https://claude.ai/code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, &lt;a href="https://opencode.ai/" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt;, &lt;a href="https://openai.com/codex/" rel="noopener noreferrer"&gt;OpenAI Codex&lt;/a&gt;, &lt;a href="https://github.com/features/copilot/cli" rel="noopener noreferrer"&gt;GitHub Copilot CLI&lt;/a&gt;, &lt;a href="https://developers.google.com/gemini-code-assist/docs/gemini-cli" rel="noopener noreferrer"&gt;Gemini CLI&lt;/a&gt;, and others using Model Context Protocol for tool access. These systems can write code, read logs, trace execution, run tests, and suggest fixes on their own.&lt;/p&gt;

&lt;p&gt;In fact, this future might be closer than many realize. Power users of these coding agents are already running armies of autonomous AI systems doing exactly what this article describes: writing code, debugging issues, running tests, and iterating on solutions with minimal human intervention. What seems theoretical to some is already the daily workflow for others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Picture a production incident at 3am:&lt;/em&gt;&lt;/strong&gt; The code was written by AI. Now AI traces execution paths, connects logs, generates hypotheses, identifies root cause, and implements fixes. Faster than a human team could assemble.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The human role:&lt;/em&gt;&lt;/strong&gt; Validate the evidence. Review the root cause analysis. Confirm the fix doesn’t break business logic. Maintain the AI systems themselves, making sure they stay observable and their reasoning stays transparent.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Inevitability of AI Dependency
&lt;/h2&gt;

&lt;p&gt;Some might argue this creates dangerous dependency on AI systems. But that framing might miss what’s already happening.&lt;/p&gt;

&lt;p&gt;We’re already building AI into our development workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Codebases structured around specific AI tools&lt;/li&gt;
&lt;li&gt;Documentation written for AI consumption (.cursorrules, CLAUDE.md files)&lt;/li&gt;
&lt;li&gt;Workflows that assume AI availability for testing, refactoring, debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 5-10 years, developing without AI assistance might be like suggesting we go back to punch cards. The dependency isn’t optional. It becomes the baseline. The real questions become: Which AI systems do we depend on? Can we switch between them? Can we verify they’re working correctly?&lt;/p&gt;




&lt;h2&gt;
  
  
  Regulations Will Evolve Too
&lt;/h2&gt;

&lt;p&gt;It’s easy to assume regulations would prevent this shift. But regulations adapt to technology changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Historical precedent:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aviation moved from “humans must understand every component” to accepting fly-by-wire systems with software doing the work&lt;/li&gt;
&lt;li&gt;Medical devices went from purely mechanical to software-controlled with verification frameworks&lt;/li&gt;
&lt;li&gt;Financial systems moved from human auditors checking every transaction to automated monitoring with sample checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Future regulatory frameworks might require:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI-generated code along with verifiable reasoning chains&lt;/li&gt;
&lt;li&gt;Audit logs of AI decision-making processes&lt;/li&gt;
&lt;li&gt;Critical path code validated by independent AI systems from different providers&lt;/li&gt;
&lt;li&gt;Human verification of AI findings on representative samples with statistical confidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not “humans must read all code,” but “humans must verify the verification system works.”&lt;/p&gt;

&lt;p&gt;Think about compliance itself. Why would regulatory oversight stay purely human when everything else is AI-assisted? AI could constantly monitor codebases for security holes, privacy violations, and accessibility issues. It could generate compliance reports with evidence and flag potential violations before deployment. Human regulators could review AI-generated summaries and dig deep only on flagged issues.&lt;/p&gt;

&lt;p&gt;This might actually increase regulatory coverage. Human regulators can’t review every line of code at every company. AI could, with human oversight on methods and findings.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Economic Pressure
&lt;/h2&gt;

&lt;p&gt;Here’s where theory meets observable reality: if AI can handle writing code, reviewing it, debugging it, and checking compliance, the money calculation changes fundamentally.&lt;/p&gt;

&lt;p&gt;Today, optimizing code for human readability makes sense because humans write and maintain most code. But if AI writes and analyzes code continuously while humans review it occasionally, the economics flip. When AI generates and processes code thousands of times more frequently than humans read it, optimizing for machine efficiency over human comprehension starts to make business sense.&lt;/p&gt;

&lt;p&gt;At big enough scale, more compact code means real cost savings. Not just in direct API costs, but in processing speed. Shorter code means faster analysis, which means faster deployment, which means competitive advantage.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Risks (And How to Manage Them)
&lt;/h2&gt;

&lt;p&gt;The most concerning scenario isn’t the end state. It’s the transition period. Imagine AI that’s 95% reliable. Good enough that teams start depending on it. Not reliable enough to fully trust. You’d end up with code humans can’t easily understand, AI that’s wrong 1 in 20 times, and no one able to catch the errors.&lt;/p&gt;

&lt;p&gt;If this transition happens, the real risks are more subtle than simple AI dependency:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Oligopoly control:&lt;/em&gt;&lt;/strong&gt; If 2-3 providers dominate, they control the means of production for all software. Pricing power, feature control, and policy decisions become centralized in ways even cloud providers don’t achieve. This makes multi-provider strategies essential. Organizations will need to maintain relationships with multiple AI providers, similar to multi-cloud strategies today. The ability to switch or run parallel systems becomes a competitive requirement, not a nice-to-have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Systemic correlation:&lt;/em&gt;&lt;/strong&gt; If everyone uses the same AI models, we get correlated failures. A bug in the model produces bugs across millions of codebases at once. A security flaw in AI reasoning becomes a universal vulnerability. Critical systems will need multi-model verification, requiring validation from AI systems built on different architectures, trained on different data, from different providers. Like how aircraft use redundant systems from different manufacturers. When safety matters, you don’t rely on a single point of failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Knowledge concentration:&lt;/em&gt;&lt;/strong&gt; As AI handles more routine work, the broad human ability to deeply understand code could shrink. When AI fails or produces novel bugs, organizations need experts who can debug from first principles. The danger is underinvesting in this specialized expertise because “AI can handle it.” Like GPS navigation. It works great until it doesn’t, and then you’re lost if you never learned to read maps.&lt;/p&gt;

&lt;p&gt;But here’s the thing: SMEs (Subject Matter Experts) with deep programming knowledge don’t become less valuable. They become more valuable. Their role shifts from writing all code to validating AI reasoning, handling edge cases, maintaining AI systems, and serving as escalation when AI fails. Organizations that maintain this expertise gain competitive advantage. The skill doesn’t disappear. It concentrates and becomes more strategic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Verification capability gap:&lt;/em&gt;&lt;/strong&gt; If humans lose the ability to meaningfully verify AI reasoning, trust becomes impossible to check and errors compound undetected. This is where the industry needs to pivot value, effort, and expertise toward verification rather than implementation. The critical skill becomes “can you effectively validate AI reasoning and evidence?” rather than “can you write this code from scratch?”&lt;/p&gt;

&lt;p&gt;This is a different skillset but not a lesser one. It requires deep understanding of correctness, edge cases, and system behavior. The developers who master verification in an AI-native world will be as valuable as the architects and senior engineers of today. Organizations that invest in building these verification capabilities during the transition will be better positioned than those that assume “AI can handle it.”&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Might Lead
&lt;/h2&gt;

&lt;p&gt;If this theory plays out, we might see a gradual evolution in how we think about code quality:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short term (now):&lt;/strong&gt; AI assists most developers with writing and debugging routine code; power users already running autonomous agent workflows. Humans maintain deep understanding and do final reviews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Medium term (5-10 years):&lt;/strong&gt; AI writes and debugs most code, humans verify reasoning and outcomes through AI-provided evidence, code readability starts to matter less.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Long term (15+ years):&lt;/strong&gt; AI-written machine-optimized code becomes standard for many areas, humans verify through abstraction layers (requirements, tests, reasoning chains), regulations adapt to AI-native development.&lt;/p&gt;

&lt;p&gt;This could create a split:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human-readable code&lt;/strong&gt; for systems where human understanding stays critical: regulated industries with strict oversight, safety-critical systems where failures have serious consequences, long-lived systems needing institutional knowledge.&lt;br&gt;
**&lt;br&gt;
Machine-optimized code** for systems where behavior and performance matter more than readability: internal tools, high-iteration low-stakes systems, performance-critical systems where optimization benefits outweigh readability costs.&lt;/p&gt;

&lt;p&gt;The qualities we optimize for wouldn’t disappear. They’d redistribute based on who the primary reader of the code is.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Might This Become Real?
&lt;/h2&gt;

&lt;p&gt;The shift becomes possible when trusting AI explanation plus cited evidence becomes more reliable than trusting your own ability to read and understand code.&lt;/p&gt;

&lt;p&gt;For routine work, we might be approaching that threshold. For high-stakes systems, we’re probably far from it. The path forward likely requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI systems that can reliably explain and justify their reasoning with verifiable evidence&lt;/li&gt;
&lt;li&gt;Standards for switching between AI providers&lt;/li&gt;
&lt;li&gt;Verification frameworks proving AI reliability at statistical confidence levels&lt;/li&gt;
&lt;li&gt;Regulatory changes creating accountability without requiring human understanding of every line&lt;/li&gt;
&lt;li&gt;Keeping human baseline knowledge during the transition to catch AI failures&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Maybe I’m wrong. Maybe human understanding will always stay central. Maybe the verification overhead will exceed efficiency gains. Maybe regulations will require human-readable code regardless of technical ability. Maybe AI reliability will plateau before reaching the threshold this theory requires.&lt;/p&gt;

&lt;p&gt;But even if this specific scenario doesn’t happen, the basic question stays worth exploring: When we say “good code,” who are we optimizing for? And if that audience changes, shouldn’t our definition of quality change with it?&lt;/p&gt;

&lt;p&gt;The key requirement is reliability. Without AI systems that can consistently and verifiably write, debug, and troubleshoot code on their own, this stays theoretical. But if that threshold is reached, the money pressure and practical advantages might drive this shift faster than we expect.&lt;/p&gt;

&lt;p&gt;We’re not there yet. But the direction suggests it’s worth thinking through the implications now, while we still have the knowledge and ability to build the right accountability systems for whatever comes next.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>watercooler</category>
      <category>software</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Using Amazon Bedrock with OpenWebUI when working with sensitive data</title>
      <dc:creator>John Reilly Pospos (JP)</dc:creator>
      <pubDate>Sun, 18 Aug 2024 09:02:34 +0000</pubDate>
      <link>https://dev.to/aws-builders/using-amazon-bedrock-with-openwebui-when-working-with-sensitive-data-40am</link>
      <guid>https://dev.to/aws-builders/using-amazon-bedrock-with-openwebui-when-working-with-sensitive-data-40am</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I've been using OpenWebUI and local Llama models to help me with heavy lifting tasks like analyzing, classifying, fact-checking, and summarizing against documents, and personal financial records. It's been a valuable tool, allowing me to keep all that confidential data secured on my local machine. But I've run into one major frustration - my laptop just doesn't have the computing power to handle the larger language models I need for this kind of complex analysis. I was stuck using the smaller 7B models, which still took precious seconds to respond.&lt;/p&gt;

&lt;p&gt;I was about to subscribe to a cloud-based service, resigning myself to having my sensitive data leave my local environment. But then I had a thought - what if I could leverage the power of a cloud-based service while still keeping my data secure and private? That's when I realized I could use Amazon Bedrock.&lt;/p&gt;

&lt;p&gt;I already knew about Amazon Bedrock and had access to an AWS account. It only recently dawned on me that I could combine Bedrock's advanced foundation models with the privacy-focused approach of OpenWebUI. The idea of tapping into Bedrock's powerful LLM (Large Language Model) capabilities, while still maintaining the local data storage and confidentiality of OpenWebUI, was exactly what I needed to overcome the performance limitations of my laptop and keep my information secure.&lt;/p&gt;

&lt;p&gt;Now, instead of being stuck with the limited performance of smaller local models, I can seamlessly integrate OpenWebUI with Amazon Bedrock. This allows me to leverage the wider range of high-performing foundation models available through Bedrock, without exposing my sensitive documents, financial records, or other proprietary information to the public cloud.&lt;/p&gt;

&lt;p&gt;Although my interactions with Amazon Bedrock happen within my AWS account, all my conversations and data remain locally stored because I'm using a local vector database with OpenWebUI. This ensures my confidential data never leaves my controlled environment.&lt;/p&gt;

&lt;p&gt;It's been a game-changer for the way I approach heavy-lifting tasks that require the power of LLMs. That's why I'm excited to share this powerful combination of Amazon Bedrock and OpenWebUI, and how it can benefit you when handling sensitive and proprietary information. Let's dive in.&lt;/p&gt;




&lt;h2&gt;
  
  
  What are Amazon Bedrock and OpenWebUI?
&lt;/h2&gt;

&lt;p&gt;To dive into the details of how these two tools work together, let's first take a closer look at Amazon Bedrock and OpenWebUI.&lt;/p&gt;

&lt;p&gt;Amazon Bedrock is a fully managed service that provides access to a wide range of high-performing foundation models (FMs) from leading AI providers like Anthropic, Cohere, and Stability AI. This allows you to select the best model for your specific use case, whether you're analyzing internal documents, questioning financial records, or processing customer information. Importantly, Amazon Bedrock is designed with a focus on security, privacy, and responsible AI practices, ensuring your sensitive data remains protected.&lt;/p&gt;

&lt;p&gt;On the flip side, OpenWebUI is an open-source project that gives you a user-friendly web interface for working with LLMs like LLaMA and GPT. This makes OpenWebUI an ideal companion when handling sensitive information, as it keeps all user data stored locally on your device and prevents any external access or exposure.&lt;/p&gt;

&lt;p&gt;By leveraging the capabilities of both Amazon Bedrock and OpenWebUI, you can tap into advanced LLM-powered functionality while prioritizing data security and privacy. Let's explore how these two tools work together in more detail.&lt;/p&gt;




&lt;h2&gt;
  
  
  What makes them ideal for handling sensitive data?
&lt;/h2&gt;

&lt;p&gt;Now that we've covered what Amazon Bedrock and OpenWebUI are, let's dive into what makes them ideal for handling sensitive data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenWebUI:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Offline-First Design&lt;/strong&gt;: OpenWebUI’s offline-first approach helps to isolate user data from network-based threats, making it a strong option for handling sensitive information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict Confidentiality&lt;/strong&gt;: OpenWebUI emphasizes "strict confidentiality and no external requests," ensuring that your data remains secure and private.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent Open-Source&lt;/strong&gt;: Being open-source, OpenWebUI allows you to audit its codebase, giving you the ability to verify its data handling practices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Amazon Bedrock:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compliance with Standards&lt;/strong&gt;: Bedrock, as part of AWS, complies with stringent security standards such as FedRAMP, SOC, ISO, and HIPAA, making it suitable for industries with high compliance requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure Model Deployment&lt;/strong&gt;: Amazon Bedrock leverages isolated accounts to ensure that model providers do not have access to customer data, enhancing security for sensitive information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robust Data Protection&lt;/strong&gt;: Bedrock is designed to protect customer data with encryption and secure transmission. While data may be transmitted over the public internet, it is safeguarded by AWS’s robust security measures, ensuring data is protected both in transit and at rest.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These security and privacy-focused features of OpenWebUI and Amazon Bedrock make them a powerful combination for handling sensitive data with confidence.&lt;/p&gt;




&lt;h2&gt;
  
  
  Integrating OpenWebUI with Amazon Bedrock
&lt;/h2&gt;

&lt;p&gt;Now that we've explored the security and privacy features of OpenWebUI and Amazon Bedrock, let's dive into the process of integrating the two tools. To get started, you'll need to set up both OpenWebUI and the Bedrock Access Gateway on your local machine. Once that's done, you can configure OpenWebUI to integrate with Bedrock, allowing you to interact with models like Claude3-Sonnet and use features like Cohere.Embedding - all while keeping your sensitive data secure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk6nm42aa1xa6z688ahl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmk6nm42aa1xa6z688ahl.png" alt="OpenWebUI and Amazon Bedrock Integration diagram" width="800" height="1044"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But why integrate OpenWebUI with Amazon Bedrock when you could just use local LLMs like LLaMA and still remain secure? There are a few key reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access to Larger and More Capable Models&lt;/strong&gt;: While LLMs like LLaMA are powerful, Amazon Bedrock provides access to an even wider range of leading foundation models, including those from providers like Anthropic, Cohere, and Stability AI. This allows you to leverage more advanced and capable generative AI capabilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Responses with Limited Local Resources&lt;/strong&gt;: Running LLMs can be resource-intensive, especially on local devices with limited CPU, memory, and GPU capabilities. By integrating with Amazon Bedrock, you can offload the heavy computational workload to the cloud, ensuring fast responses even when your local hardware is constrained.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ongoing Model Updates and Improvements&lt;/strong&gt;: As a fully managed service, Amazon Bedrock handles the maintenance and updates of the foundation models, ensuring you always have access to the latest versions and improvements. This can be challenging to manage on your own when working with local models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By combining the benefits of OpenWebUI's offline-first approach with the powerful and scalable capabilities of Amazon Bedrock, you can access advanced generative AI capabilities while keeping your sensitive and proprietary data secure.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;p&gt;To set up OpenWebUI on your local machine, follow these steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Download and Install OpenWebUI
&lt;/h3&gt;

&lt;p&gt;Head over to the OpenWebUI GitHub repository, clone their repo and install &lt;code&gt;open-webui&lt;/code&gt; by running the commands below;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This command clones the open-webui repository from GitHub.&lt;/span&gt;
git clone https://github.com/open-webui/open-webui.git

&lt;span class="c"&gt;# This command changes the current working directory to the cloned repository.&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;open-webui

&lt;span class="c"&gt;# This command copies the .env.example file to create a new .env file. The .env file is used to store environment variables for the application.&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-RPp&lt;/span&gt; .env.example .env

&lt;span class="c"&gt;# This command installs the required Node.js dependencies for the frontend part of the application.&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# This command builds the frontend code for the application.&lt;/span&gt;
npm run build

&lt;span class="c"&gt;# This command changes the current working directory to the backend folder.&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ./backend

&lt;span class="c"&gt;# This command activates the Pipenv virtual environment for the backend code.&lt;/span&gt;
pipenv shell

&lt;span class="c"&gt;# This command installs the required Python dependencies for the backend part of the application.&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="nt"&gt;-U&lt;/span&gt;

&lt;span class="c"&gt;# Start the application&lt;/span&gt;
bash start.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes well you should see something similar to the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9gz5tl40r7y6ae3g3tdm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9gz5tl40r7y6ae3g3tdm.png" alt="OpenWebUI successful initialization" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;📝 NOTE&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;There are other and much easier ways to install &lt;code&gt;open-webui&lt;/code&gt;, go to their &lt;a href="https://docs.openwebui.com/getting-started/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; page to see the recommended installation option.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Login to your local OpenWebUI installation
&lt;/h3&gt;

&lt;p&gt;Open your browser and navigate to &lt;code&gt;http://0.0.0.0:8080&lt;/code&gt;, since this is your first time accessing you will need to sign up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5srfyveawpiicx31abh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl5srfyveawpiicx31abh.png" alt="Sign-up" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;📝 NOTE&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;This sign-up is only for your local instance of &lt;code&gt;open-webui&lt;/code&gt; and totally unrelated to signing-up to &lt;code&gt;https://openwebui.com/&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Download and Install Bedrock Access Gateway
&lt;/h3&gt;

&lt;p&gt;Head over to the &lt;code&gt;bedrock-access-gateway&lt;/code&gt; GitHub repository, clone their repo and install by running the commands below;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This command clones the bedrock-access-gateway repository from GitHub.&lt;/span&gt;
git clone https://github.com/aws-samples/bedrock-access-gateway.git

&lt;span class="c"&gt;# This command changes the current working directory to the cloned repository.&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;src

docker build &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile_ecs &lt;span class="nt"&gt;-t&lt;/span&gt; bedrock-access-gateway &lt;span class="nb"&gt;.&lt;/span&gt;

docker run &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;replace-with-access-key-id&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;replace-with-secret-access-key&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;replace-with-region&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;replace-with-region&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:80 &lt;span class="se"&gt;\&lt;/span&gt;
    bedrock-access-gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything goes well, you should see something similar to the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k4l7o4hj3c9drkx7y6m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k4l7o4hj3c9drkx7y6m.png" alt="Bedrock Access Gateway successful initialization" width="800" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;📝 NOTE&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Make sure you have sufficient access to Bedrock and you have requested access to the base models you want to use via the Amazon Bedrock console.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Connect the OpenWebUI to Amazon Bedrock
&lt;/h3&gt;

&lt;p&gt;This allows you to chat with Bedrock models. Within OpenWebUI, go to "Settings" &amp;gt; "Admin Settings" &amp;gt; "Connections". Here, Update the "OpenAI API" section's &lt;code&gt;API Base URL&lt;/code&gt; to point to your &lt;code&gt;bedrock-access-gateway&lt;/code&gt;'s URL which is &lt;code&gt;http://localhost:8081/api/v1&lt;/code&gt; and update the &lt;code&gt;API Key&lt;/code&gt; to &lt;code&gt;bedrock-access-gateway&lt;/code&gt;'s default API key which is &lt;code&gt;bedrock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then click on the verify connection icon as highlighted on the image below. Upon clicking it, you should see a green popup saying "Server connection verified" Once verified, click the "Save" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowj99f575y0i5oxb4wob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowj99f575y0i5oxb4wob.png" alt="Connect the OpenWebUI to Amazon Bedrock" width="800" height="840"&gt;&lt;/a&gt;   &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;📝 NOTE&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;For added security, store the API key you want to use in SSM and reference the SSM parameter name by defining the &lt;code&gt;API_KEY_PARAM_NAME&lt;/code&gt; environment variable when running the Docker container.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  5. Connect the OpenWebUI Embedding Model Engine to Amazon Bedrock
&lt;/h3&gt;

&lt;p&gt;After connecting OpenWebUI to Amazon Bedrock, the next step is to integrate the embedding model engine. This allows you to use Bedrock's embedding models to enhance document processing. Within OpenWebUI, go to "Settings" &amp;gt; "Admin Settings" &amp;gt; "Documents". Here, Update the "Embedding Model Engine" section's &lt;code&gt;API Base URL&lt;/code&gt; to point to your &lt;code&gt;bedrock-access-gateway&lt;/code&gt;'s URL which is &lt;code&gt;http://localhost:8081/api/v1&lt;/code&gt; and update the &lt;code&gt;API Key&lt;/code&gt; to &lt;code&gt;bedrock-access-gateway&lt;/code&gt;'s default API key which is &lt;code&gt;bedrock&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Next, you need to pick which embedding model to use by setting the &lt;code&gt;Embedding Model&lt;/code&gt; to either use &lt;code&gt;cohere.embed-english-v3&lt;/code&gt; or &lt;code&gt;cohere.embed-multilingual-v3&lt;/code&gt;. Before you save our changes, make sure you re-import all documents by clicking on "Reset Upload Directory" and "Reset Vector Storage", then simply re-upload your existing documents. &lt;/p&gt;

&lt;p&gt;Once that is done, click on the "Save" button. With the OpenWebUI-Bedrock embedding integration now set up, you're ready to start leveraging the advanced capabilities of these tools for your document processing needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fware98id92uycxr5oxxv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fware98id92uycxr5oxxv.png" alt="Connect the OpenWebUI Emdedding Model Engine to Amazon Bedrock" width="800" height="840"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Start Interacting with Bedrock Models
&lt;/h3&gt;

&lt;p&gt;With the OpenWebUI-Bedrock integration now set up, you can start tapping into the powerful capabilities of Amazon Bedrock's foundation models. This includes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chatting with Bedrock Models:&lt;/strong&gt; Within the OpenWebUI interface, you can now engage in conversations with advanced language models like Anthropic's Claude3. Simply select the desired Bedrock model from the available options and start chatting, all while keeping your sensitive data securely stored on your local machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1g4pp1jwzjp6drez52a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1g4pp1jwzjp6drez52a.png" alt="Choosing Bedrock Models to chat with" width="800" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start chatting once you've chosen your bedrock model of choice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wvz21v9httxlsb21ou6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wvz21v9httxlsb21ou6.png" alt="Chatting with Bedrock Models" width="800" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leveraging Bedrock Embedding Models:&lt;/strong&gt; In addition to language modeling, you can also utilize Bedrock's high-quality embedding models to enhance your document processing workflows. Options like Cohere's Embed-English and Embed-Multilingual are available through the OpenWebUI integration, allowing you to generate rich vector representations of your text data. Whenever you upload new documents to OpenWebUI it should now use Cohere's Embedding model through Amazon Bedrock.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqaexesnukeamgdbwx87x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqaexesnukeamgdbwx87x.png" alt="Uploading documents" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;📝 NOTE&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;You can validate this by checking &lt;code&gt;bedrock-access-gateway&lt;/code&gt;'s logs as you upload a document.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Once a document is uploaded, you can start questioning it! To do that, you can reference any document by using the &lt;code&gt;#&lt;/code&gt; sign on a new chat window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30756udjbvvm7rcb244b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30756udjbvvm7rcb244b.png" alt="Choosing a document" width="800" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you see a document has been chosen and have started quesitoning it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F357u6vt7yzuz0gujufsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F357u6vt7yzuz0gujufsk.png" alt="Document chosen" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here you see claude3 responding.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbtwqxi9ilibug1qhdwg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbtwqxi9ilibug1qhdwg.png" alt="Questioning chosen document" width="800" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By seamlessly connecting OpenWebUI to Amazon Bedrock, you have the best of both worlds - the privacy and security of a local, offline-first tool combined with access to a wide range of advanced generative AI capabilities. This empowers you to handle your sensitive data with confidence while tapping into the cutting-edge functionality provided by Bedrock.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With the OpenWebUI-Bedrock integration set up, you're now ready to start leveraging the powerful capabilities of these two tools to handle your sensitive data with ease. The combination of OpenWebUI's privacy-focused approach and Bedrock's advanced foundation models, along with its robust data protection features, provides a comprehensive solution for tackling your most complex and confidential tasks.&lt;/p&gt;

&lt;p&gt;Amazon Bedrock's compliance with stringent security standards like FedRAMP, SOC, ISO, and HIPAA, as well as its secure model deployment and robust data protection mechanisms, ensures that your sensitive data remains safeguarded throughout the process. By seamlessly connecting OpenWebUI to Bedrock, you have the best of both worlds - the privacy and security of a local, offline-first tool combined with access to a wide range of cutting-edge generative AI capabilities.&lt;/p&gt;

&lt;p&gt;I'm confident that this integration will be a game-changer for the way you approach sensitive and confidential data tasks. With the powerful combination of OpenWebUI and Amazon Bedrock at your fingertips, you can tackle even your most complex challenges while maintaining the highest standards of security and privacy. As I'll be using this setup as my daily driver, I'll be sure to report back on the cost-effectiveness of running this configuration for a month. Please let me know if you have any other questions as you get started!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>llm</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
