ADR-0004: Infrastructure Layout
Decision to use .infra/ convention for infrastructure-as-code in application repositories.
Status: Accepted
Date: 2025-12-16
Deciders: Engineering Leadership
Context
Ontopix manages infrastructure-as-code across multiple repositories. Without standardization:
Discovery Problems:
- Engineers don't know where to find Terraform code
- Inconsistent locations (root, terraform/, infra/, infrastructure/, etc.)
- Hard to navigate new repositories
- Agents can't reliably locate infrastructure
Operational Problems:
- No standard way to operate infrastructure
- Direct
terraforminvocations (bypassing safety checks) - Inconsistent state management
- No clear owner for infrastructure changes
Safety Problems:
- Easy to accidentally apply changes
- Production infrastructure not clearly marked
- No standard approval workflow
- Agents could make dangerous changes
We need a standard location and operational interface for infrastructure-as-code.
Decision
We will adopt the .infra/ convention for infrastructure-as-code in application repositories.
Core Rules
- Application repositories that provision cloud resources MUST place Terraform in
.infra/ - The central
infrarepository is EXEMPT (it IS infrastructure, doesn't follow.infra/rule) - Infrastructure operations MUST be exposed through Taskfile tasks (
infra:*) - Agents MUST request human approval before infrastructure changes
Directory Structure
my-service/
├─ .infra/
│ ├─ main.tf
│ ├─ variables.tf
│ ├─ outputs.tf
│ ├─ backend.tf
│ └─ modules/
├─ src/
├─ Taskfile.yaml
├─ AGENTS.md
└─ README.md
Taskfile Integration
Infrastructure operations exposed as:
infra:plan:
desc: Show Terraform plan
dir: .infra
cmds: [terraform init, terraform plan]
infra:apply:
desc: Apply infrastructure changes (requires approval)
dir: .infra
cmds: [terraform init, terraform apply]
infra:destroy:
desc: Destroy infrastructure (dangerous)
dir: .infra
preconditions:
- sh: "[ '{{.CONFIRM}}' = 'yes' ]"
cmds: [terraform destroy]
The Central infra Repository Exception
The infra repository:
- Provisions foundational platform infrastructure (dns, iam, cost alerts, etc.)
- Does NOT use
.infra/convention - Organizes Terraform at root or in top-level directories
- Is the ONLY exception to the rule
Why this exception?
The infra repository IS infrastructure. It doesn't contain application code that needs infrastructure; it defines the platform.
Rationale
Why .infra/ Instead of Alternatives?
Considered locations:
infra/— Too prominent, conflicts with central infra repo nameterraform/— Tool-specific, doesn't age wellinfrastructure/— Too verbose.terraform/— Conflicts with Terraform's internal directory- Root directory — Clutters repository root
Why .infra/ wins:
- Hidden (leading dot) but not obscure
- Short and memorable
- Tool-agnostic (works if we switch from Terraform)
- Doesn't conflict with central infra repo
- Clear purpose
Why Separate .infra/ from Application Code?
Separation of Concerns:
- Infrastructure is different from application code
- Different lifecycle (infra changes less frequently)
- Different ownership (platform team often involved)
- Different review requirements
Safety:
- Infrastructure changes visible in PRs
- Clear boundary for sensitive operations
- Agents know where dangerous operations live
- Easy to apply additional protections (CODEOWNERS)
Discoverability:
- Standard location across repositories
- Engineers immediately know where to find infrastructure
- Agents can locate infrastructure reliably
Why Taskfile Integration?
Safety:
- Tasks encapsulate preconditions
infra:destroyrequires explicit confirmation- Agents use tasks, not direct Terraform
- Production deployments require approval
Consistency:
- Same operational interface as application code
task infra:applyworks everywhere- No need to remember Terraform flags
Documentation:
task --listshows infrastructure operations- Self-documenting interface
- Clear intent (plan vs apply vs destroy)
Why Central infra Exception?
The infra repository is fundamentally different:
- It IS the platform infrastructure
- Terraform IS the primary content
Avoiding confusion:
.infra/ininfrarepo would be redundant- Would nest unnecessarily
- Unclear what "application" means in this context
Consequences
Positive
Consistency:
- Same location across all application repositories
- Predictable for engineers and agents
- Easy onboarding
Safety:
- Clear boundary for infrastructure
- Taskfile enforces preconditions
- Agents request approval
- CODEOWNERS can require platform team review
Maintainability:
- Infrastructure isolated from application code
- Clear ownership
- Easy to navigate
Operational Clarity:
- Standard Taskfile interface
- Self-documenting operations
- Safe by default
Negative
Hidden Directory:
- Leading dot hides directory
- Engineers must know to look for it
- Not visible in some file explorers
Additional Convention:
- Another thing to remember
- Can be forgotten in new repositories
- Requires discipline to enforce
Migration Effort:
- Existing repositories need restructuring
- Terraform paths may need updating
- State may need migration
Mitigations
Documentation:
- Principles clearly explain
.infra/ - Templates include
.infra/structure - AGENTS.md references infrastructure location
Tooling:
- CI validates
.infra/exists (if applicable) - Templates generate correct structure
- Automated checks for structure
- Taskfile provides consistent operational interface
Visibility:
- Document in README where infrastructure lives
- AGENTS.md explains infrastructure location
task --listshows infra tasks
Migration Support:
- Migration guide provided
- Terraform state migration documented
- Incremental migration acceptable
Alternatives Considered
Alternative 1: Root Directory
Rejected because:
- Clutters repository root
- Mixes application and infrastructure
- Hard to apply different permissions
- Confusing for newcomers
Alternative 2: infra/ Subdirectory
Rejected because:
- Conflicts with central
infrarepository name - Too prominent (not hidden)
- Confusion about which "infra" is which
Alternative 3: terraform/ Subdirectory
Rejected because:
- Tool-specific (doesn't age well)
- Vendor lock-in perception
- May need to support other IaC tools
Alternative 4: Monorepo Infrastructure Directory
Rejected as default because:
- Doesn't work for distributed repositories
- Overly centralized
- Can still be used alongside
.infra/if needed
Alternative 5: Separate Infrastructure Repositories
Rejected as default because:
- Separates infrastructure from application too much
- Harder to keep in sync
- More repositories to manage
- Can still be used for complex cases
References
- Infrastructure Layout Pattern
- Taskfile Contract Pattern
- Repository Structure Pattern
- AI Agent Entrypoint Pattern
Success Criteria
This decision is successful if:
- Application repositories consistently use
.infra/ - Engineers immediately know where to find infrastructure
- Agents reliably locate infrastructure
- Infrastructure operations go through Taskfile
- Human approval enforced for dangerous operations
- Migration effort is manageable